Compare commits
	
		
			11 Commits
		
	
	
		
			62e16219f7
			...
			723976b37e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 723976b37e | |||
| 361b84f31f | |||
| 70892aa980 | |||
| 10eec286fa | |||
| 894b1884eb | |||
| d2bab5fd9e | |||
| f94f369547 | |||
| 107ec4958f | |||
| a22047e84e | |||
| 80b7d5b988 | |||
| 2ab896bbac | 
							
								
								
									
										41
									
								
								context-helpers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								context-helpers.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||
| // All rights reserved.
 | ||||
| 
 | ||||
| // context-helpers.go
 | ||||
| package expr | ||||
| 
 | ||||
| func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) { | ||||
| 	if sourceCtx != nil { | ||||
| 		clonedCtx = sourceCtx.Clone() | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func exportVar(ctx ExprContext, name string, value any) { | ||||
| 	if name[0] == '@' { | ||||
| 		name = name[1:] | ||||
| 	} | ||||
| 	ctx.setVar(name, value) | ||||
| } | ||||
| 
 | ||||
| func exportFunc(ctx ExprContext, name string, info ExprFunc) { | ||||
| 	if name[0] == '@' { | ||||
| 		name = name[1:] | ||||
| 	} | ||||
| 	ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) | ||||
| } | ||||
| 
 | ||||
| func exportObjects(destCtx, sourceCtx ExprContext) { | ||||
| 	exportAll := isEnabled(sourceCtx, control_export_all) | ||||
| 	// Export variables
 | ||||
| 	for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { | ||||
| 		refValue, _ := sourceCtx.GetVar(refName) | ||||
| 		exportVar(destCtx, refName, refValue) | ||||
| 	} | ||||
| 	// Export functions
 | ||||
| 	for _, refName := range sourceCtx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) { | ||||
| 		if info, _ := sourceCtx.GetFuncInfo(refName); info != nil { | ||||
| 			exportFunc(destCtx, refName, info) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										62
									
								
								data-cursor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								data-cursor.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||
| // All rights reserved.
 | ||||
| 
 | ||||
| // data-cursors.go
 | ||||
| package expr | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	initName    = "init" | ||||
| 	nextName    = "next" | ||||
| 	currentName = "current" | ||||
| ) | ||||
| 
 | ||||
| type dataCursor struct { | ||||
| 	ds          map[any]*term | ||||
| 	ctx         ExprContext | ||||
| 	index       int | ||||
| 	resource    any | ||||
| 	nextFunc    Functor | ||||
| 	currentFunc Functor | ||||
| } | ||||
| 
 | ||||
| func newDataCursor(ctx ExprContext) (dc *dataCursor) { | ||||
| 	dc = &dataCursor{ | ||||
| 		index: -1, | ||||
| 		ctx:   ctx.Clone(), | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) String() string { | ||||
| 	return "$(...)" | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
 | ||||
| 	ctx := cloneContext(dc.ctx) | ||||
| 	if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil { | ||||
| 		err = io.EOF | ||||
| 	} | ||||
| 	exportObjects(dc.ctx, ctx) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
 | ||||
| 	ctx := cloneContext(dc.ctx) | ||||
| 	if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{}); err == nil { | ||||
| 		if item == nil { | ||||
| 			err = io.EOF | ||||
| 		} else { | ||||
| 			dc.index++ | ||||
| 		} | ||||
| 	} | ||||
| 	exportObjects(dc.ctx, ctx) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Index() int { | ||||
| 	return dc.index | ||||
| } | ||||
							
								
								
									
										19
									
								
								expr_test.go
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								expr_test.go
									
									
									
									
									
								
							| @ -27,11 +27,22 @@ func TestExpr(t *testing.T) { | ||||
| 	succeeded := 0 | ||||
| 	failed := 0 | ||||
| 
 | ||||
| 	// inputs1 := []inputType{
 | ||||
| 	// 	/*   1 */ {`0?{}`, nil, nil},
 | ||||
| 	// }
 | ||||
| 	inputs1 := []inputType{ | ||||
| 		/*   1 */ {` | ||||
| 		ds={ | ||||
| 			"init":func(end){@end=end; @current=0 but true}, | ||||
| 			"current":func(){current}, | ||||
| 			"next":func(){ | ||||
| 				((next=current+1) <= end) ? [true] {@current=next but current} :: {nil} | ||||
| 			} | ||||
| 		}; | ||||
| 		it=$(ds,3); | ||||
| 		it++; | ||||
| 		it++ | ||||
| `, int64(1), nil}, | ||||
| 	} | ||||
| 
 | ||||
| 	for i, input := range inputs { | ||||
| 	for i, input := range inputs1 { | ||||
| 		var expr Expr | ||||
| 		var gotResult any | ||||
| 		var gotErr error | ||||
|  | ||||
| @ -5,10 +5,6 @@ | ||||
| package expr | ||||
| 
 | ||||
| // -------- dict term
 | ||||
| // func newDictTermA(args ...*term) *term {
 | ||||
| // 	return newDictTerm(args)
 | ||||
| // }
 | ||||
| 
 | ||||
| func newDictTerm(args map[any]*term) *term { | ||||
| 	return &term{ | ||||
| 		tk:       *NewValueToken(0, 0, SymDict, "{}", args), | ||||
|  | ||||
| @ -27,8 +27,3 @@ func evalExpr(ctx ExprContext, self *term) (v any, err error) { | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // init
 | ||||
| // func init() {
 | ||||
| // 	registerTermConstructor(SymExpression, newExprTerm)
 | ||||
| // }
 | ||||
|  | ||||
| @ -22,7 +22,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term { | ||||
| 
 | ||||
| // -------- eval func call
 | ||||
| func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | ||||
| 	ctx := parentCtx.Clone() | ||||
| 	ctx := cloneContext(parentCtx) | ||||
| 	name, _ := self.tk.Value.(string) | ||||
| 	params := make([]any, len(self.children)) | ||||
| 	for i, tree := range self.children { | ||||
| @ -34,37 +34,12 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | ||||
| 	} | ||||
| 	if err == nil { | ||||
| 		if v, err = ctx.Call(name, params); err == nil { | ||||
| 			exportAll := isEnabled(ctx, control_export_all) | ||||
| 			// Export variables
 | ||||
| 			for _, refName := range ctx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { | ||||
| 				refValue, _ := ctx.GetVar(refName) | ||||
| 				exportVar(parentCtx, refName, refValue) | ||||
| 			} | ||||
| 			// Export functions
 | ||||
| 			for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) { | ||||
| 				if info, _ := ctx.GetFuncInfo(refName); info != nil { | ||||
| 					exportFunc(parentCtx, refName, info) | ||||
| 				} | ||||
| 			} | ||||
| 			exportObjects(parentCtx, ctx) | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func exportVar(ctx ExprContext, name string, value any) { | ||||
| 	if name[0] == '@' { | ||||
| 		name = name[1:] | ||||
| 	} | ||||
| 	ctx.setVar(name, value) | ||||
| } | ||||
| 
 | ||||
| func exportFunc(ctx ExprContext, name string, info ExprFunc) { | ||||
| 	if name[0] == '@' { | ||||
| 		name = name[1:] | ||||
| 	} | ||||
| 	ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) | ||||
| } | ||||
| 
 | ||||
| // -------- function definition term
 | ||||
| func newFuncDefTerm(tk *Token, args []*term) *term { | ||||
| 	return &term{ | ||||
| @ -107,12 +82,6 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { | ||||
| 		paramList := make([]string, 0, len(self.children)) | ||||
| 		for _, param := range self.children { | ||||
| 			paramList = append(paramList, param.source()) | ||||
| 			// if paramName, ok := param.value().(string); ok {
 | ||||
| 			// 	paramList = append(paramList, paramName)
 | ||||
| 			// } else {
 | ||||
| 			// 	err = fmt.Errorf("invalid function definition: formal param nr %d must be an identifier", i+1)
 | ||||
| 			// 	break
 | ||||
| 			// }
 | ||||
| 		} | ||||
| 		v = &funcDefFunctor{ | ||||
| 			params: paramList, | ||||
|  | ||||
| @ -6,58 +6,10 @@ package expr | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| ) | ||||
| 
 | ||||
| // -------- iterator term
 | ||||
| 
 | ||||
| const ( | ||||
| 	initName    = "init" | ||||
| 	nextName    = "next" | ||||
| 	currentName = "current" | ||||
| ) | ||||
| 
 | ||||
| type dataCursor struct { | ||||
| 	ds          map[any]*term | ||||
| 	ctx         ExprContext | ||||
| 	index       int | ||||
| 	resource    any | ||||
| 	nextFunc    Functor | ||||
| 	currentFunc Functor | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) String() string { | ||||
| 	var s string | ||||
| 	if item, err := dc.Current(); err == nil { | ||||
| 		s = fmt.Sprintf("%v", item) | ||||
| 	} else { | ||||
| 		s = "(nil)" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
 | ||||
| 	if item, err = dc.currentFunc.Invoke(dc.ctx, currentName, []any{}); err == nil && item == nil { | ||||
| 		err = io.EOF | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
 | ||||
| 	if item, err = dc.nextFunc.Invoke(dc.ctx, nextName, []any{}); err == nil { | ||||
| 		if item == nil { | ||||
| 			err = io.EOF | ||||
| 		} else { | ||||
| 			dc.index++ | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (dc *dataCursor) Index() int { | ||||
| 	return dc.index | ||||
| } | ||||
| 
 | ||||
| func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { | ||||
| 	tk.Sym = SymIterator | ||||
| 
 | ||||
| @ -125,10 +77,7 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	dc := &dataCursor{ | ||||
| 		index: -1, | ||||
| 		ctx:   ctx.Clone(), | ||||
| 	} | ||||
| 	dc := newDataCursor(ctx) | ||||
| 
 | ||||
| 	if initFunc, exists := ds[initName]; exists && initFunc != nil { | ||||
| 		var args []any | ||||
| @ -139,9 +88,12 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) { | ||||
| 		} else { | ||||
| 			args = []any{} | ||||
| 		} | ||||
| 		if dc.resource, err = initFunc.Invoke(dc.ctx, initName, args); err != nil { | ||||
| 
 | ||||
| 		initCtx := dc.ctx.Clone() | ||||
| 		if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		exportObjects(dc.ctx, initCtx) | ||||
| 	} | ||||
| 
 | ||||
| 	dc.nextFunc, _ = ds[nextName] | ||||
|  | ||||
							
								
								
									
										49
									
								
								operator-context.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								operator-context.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||
| // All rights reserved.
 | ||||
| 
 | ||||
| // operator-context-value.go
 | ||||
| package expr | ||||
| 
 | ||||
| //-------- context term
 | ||||
| 
 | ||||
| func newContextTerm(tk *Token) (inst *term) { | ||||
| 	return &term{ | ||||
| 		tk:       *tk, | ||||
| 		children: make([]*term, 0, 1), | ||||
| 		position: posPrefix, | ||||
| 		priority: priPrePost, | ||||
| 		evalFunc: evalContextValue, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func evalContextValue(ctx ExprContext, self *term) (v any, err error) { | ||||
| 	var childValue any | ||||
| 
 | ||||
| 	var sourceCtx ExprContext | ||||
| 	if len(self.children) == 0 { | ||||
| 		sourceCtx = ctx | ||||
| 	} else if childValue, err = self.evalPrefix(ctx); err == nil { | ||||
| 		if dc, ok := childValue.(*dataCursor); ok { | ||||
| 			sourceCtx = dc.ctx | ||||
| 		} | ||||
| 	} else { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if sourceCtx != nil { | ||||
| 		keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) | ||||
| 		d := make(map[string]any) | ||||
| 		for _, key := range keys { | ||||
| 			d[key], _ = sourceCtx.GetVar(key) | ||||
| 		} | ||||
| 		v = d | ||||
| 	} else { | ||||
| 		err = self.errIncompatibleType(childValue) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // init
 | ||||
| func init() { | ||||
| 	registerTermConstructor(SymDoubleDollar, newContextTerm) | ||||
| } | ||||
| @ -29,9 +29,8 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) { | ||||
| 	} else if isString(rightValue) { | ||||
| 		s, _ := rightValue.(string) | ||||
| 		v = len(s) | ||||
| 		// } else {
 | ||||
| 		// 	v = 1
 | ||||
| 		// }
 | ||||
| 	} else if it, ok := rightValue.(Iterator); ok { | ||||
| 		v = it.Index() | ||||
| 	} else { | ||||
| 		err = self.errIncompatibleType(rightValue) | ||||
| 	} | ||||
|  | ||||
| @ -4,65 +4,7 @@ | ||||
| // operator-selector.go
 | ||||
| package expr | ||||
| 
 | ||||
| // //-------- export all term
 | ||||
| 
 | ||||
| // func newSelectorTerm(tk *Token) (inst *term) {
 | ||||
| // 	return &term{
 | ||||
| // 		tk:       *tk,
 | ||||
| // 		children: make([]*term, 0, 3),
 | ||||
| // 		position: posMultifix,
 | ||||
| // 		priority: priSelector,
 | ||||
| // 		evalFunc: evalSelector,
 | ||||
| // 	}
 | ||||
| // }
 | ||||
| 
 | ||||
| // func isSelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (selectedValue any, err error) {
 | ||||
| // 	caseData, _ := caseSel.(*selectorCase)
 | ||||
| // 	if caseData.filterList == nil {
 | ||||
| // 		selectedValue, err = caseData.caseExpr.eval(ctx, false)
 | ||||
| // 	} else {
 | ||||
| // 		filterList := caseData.filterList.children
 | ||||
| // 		if len(filterList) == 0 && exprValue == int64(caseIndex) {
 | ||||
| // 			selectedValue, err = caseData.caseExpr.eval(ctx, false)
 | ||||
| // 		} else {
 | ||||
| // 			var caseValue any
 | ||||
| // 			for _, caseTerm := range filterList {
 | ||||
| // 				if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue {
 | ||||
| // 					selectedValue, err = caseData.caseExpr.eval(ctx, false)
 | ||||
| // 					break
 | ||||
| // 				}
 | ||||
| // 			}
 | ||||
| // 		}
 | ||||
| // 	}
 | ||||
| // 	return
 | ||||
| // }
 | ||||
| 
 | ||||
| // func evalSelector(ctx ExprContext, self *term) (v any, err error) {
 | ||||
| // 	var exprValue any
 | ||||
| // 	// var caseList []*term
 | ||||
| 
 | ||||
| // 	if err = self.checkOperands(); err != nil {
 | ||||
| // 		return
 | ||||
| // 	}
 | ||||
| // 	exprTerm := self.children[0]
 | ||||
| // 	if exprValue, err = exprTerm.compute(ctx); err != nil {
 | ||||
| // 		return
 | ||||
| // 	}
 | ||||
| 
 | ||||
| // 	caseList := self.children[1:]
 | ||||
| // 	for i, caseTerm := range caseList {
 | ||||
| // 		caseSel := caseTerm.value()
 | ||||
| // 		if v, err = isSelectorCase(ctx, exprValue, caseSel, i); err != nil || v != nil {
 | ||||
| // 			break
 | ||||
| // 		}
 | ||||
| // 	}
 | ||||
| // 	if err == nil && v == nil {
 | ||||
| // 		err = exprTerm.tk.Errorf("no case catches the value (%v) of the selection expression", exprValue)
 | ||||
| // 	}
 | ||||
| // 	return
 | ||||
| // }
 | ||||
| 
 | ||||
| //-------- export all term
 | ||||
| //-------- selector term
 | ||||
| 
 | ||||
| func newSelectorTerm(tk *Token) (inst *term) { | ||||
| 	return &term{ | ||||
| @ -74,7 +16,7 @@ func newSelectorTerm(tk *Token) (inst *term) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func isSelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) { | ||||
| func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) { | ||||
| 	caseData, _ := caseSel.(*selectorCase) | ||||
| 	if caseData.filterList == nil { | ||||
| 		selectedValue, err = caseData.caseExpr.eval(ctx, false) | ||||
| @ -113,7 +55,7 @@ func evalSelector(ctx ExprContext, self *term) (v any, err error) { | ||||
| 	caseList, _ := caseListTerm.value().([]*term) | ||||
| 	for i, caseTerm := range caseList { | ||||
| 		caseSel := caseTerm.value() | ||||
| 		if match, v, err = isSelectorCase(ctx, exprValue, caseSel, i); err != nil || match { | ||||
| 		if match, v, err = trySelectorCase(ctx, exprValue, caseSel, i); err != nil || match { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @ -242,6 +242,8 @@ func (self *scanner) fetchNextToken() (tk *Token) { | ||||
| 			if next, _ := self.peek(); next == '(' { | ||||
| 				tk = self.moveOn(SymDollarRound, ch, next) | ||||
| 				tk.source += ")" | ||||
| 			} else if next == '$' { | ||||
| 				tk = self.moveOn(SymDoubleDollar, ch, next) | ||||
| 			} else { | ||||
| 				tk = self.makeToken(SymDollar, ch) | ||||
| 			} | ||||
| @ -249,8 +251,8 @@ func (self *scanner) fetchNextToken() (tk *Token) { | ||||
| 			if next, _ := self.peek(); next == ')' { | ||||
| 				tk = self.moveOn(SymOpenClosedRound, ch, next) | ||||
| 			} else { | ||||
| 			tk = self.makeToken(SymOpenRound, ch) | ||||
| 		} | ||||
| 				tk = self.makeToken(SymOpenRound, ch) | ||||
| 			} | ||||
| 		case ')': | ||||
| 			tk = self.makeToken(SymClosedRound, ch) | ||||
| 		case '[': | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user