Compare commits
	
		
			14 Commits
		
	
	
		
			8144122d2c
			...
			29bc2c62a3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 29bc2c62a3 | |||
| 8eb2d77ea3 | |||
| 53bcf90d2a | |||
| f347b15146 | |||
| 115ce26ce9 | |||
| 227944b3fb | |||
| 0d01afcc9f | |||
| 00c76b41f1 | |||
| 08e0979cdd | |||
| f04f5822ec | |||
| 80d47879e9 | |||
| 985eb3d19d | |||
| 45734ab393 | |||
| c100cf349d | 
							
								
								
									
										9
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								ast.go
									
									
									
									
									
								
							| @ -10,7 +10,6 @@ import ( | |||||||
| 
 | 
 | ||||||
| type Expr interface { | type Expr interface { | ||||||
| 	Eval(ctx ExprContext) (result any, err error) | 	Eval(ctx ExprContext) (result any, err error) | ||||||
| 	eval(ctx ExprContext, preset bool) (result any, err error) |  | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -106,16 +105,10 @@ func (self *ast) Finish() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (self *ast) Eval(ctx ExprContext) (result any, err error) { | func (self *ast) Eval(ctx ExprContext) (result any, err error) { | ||||||
| 	return self.eval(ctx, true) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) { |  | ||||||
| 	self.Finish() | 	self.Finish() | ||||||
| 
 | 
 | ||||||
| 	if self.root != nil { | 	if self.root != nil { | ||||||
| 		if preset { | 		// initDefaultVars(ctx)
 | ||||||
| 			initDefaultVars(ctx) |  | ||||||
| 		} |  | ||||||
| 		if self.forest != nil { | 		if self.forest != nil { | ||||||
| 			for _, root := range self.forest { | 			for _, root := range self.forest { | ||||||
| 				if result, err = root.compute(ctx); err == nil { | 				if result, err = root.compute(ctx); err == nil { | ||||||
|  | |||||||
| @ -5,14 +5,16 @@ | |||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	typeAny      = "any" | 	TypeAny      = "any" | ||||||
| 	typeBoolean  = "boolean" | 	TypeBoolean  = "boolean" | ||||||
| 	typeFloat    = "float" | 	TypeFloat    = "float" | ||||||
| 	typeFraction = "fraction" | 	TypeFraction = "fraction" | ||||||
| 	typeHandle   = "handle" | 	TypeHandle   = "handle" | ||||||
| 	typeInt      = "integer" | 	TypeInt      = "integer" | ||||||
| 	typeItem     = "item" | 	TypeItem     = "item" | ||||||
| 	typeNumber   = "number" | 	TypeNumber   = "number" | ||||||
| 	typePair     = "pair" | 	TypePair     = "pair" | ||||||
| 	typeString   = "string" | 	TypeString   = "string" | ||||||
|  | 	TypeListOf = "list-of-" | ||||||
|  | 	TypeListOfStrings = "list-of-strings" | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -22,13 +22,11 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) { | |||||||
| 	if name[0] == '@' { | 	if name[0] == '@' { | ||||||
| 		name = name[1:] | 		name = name[1:] | ||||||
| 	} | 	} | ||||||
| 	// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
 |  | ||||||
| 	// ctx.RegisterFuncInfo(name, info)
 |  | ||||||
| 	ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params()) | 	ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func exportObjects(destCtx, sourceCtx ExprContext) { | func exportObjects(destCtx, sourceCtx ExprContext) { | ||||||
| 	exportAll := isEnabled(sourceCtx, control_export_all) | 	exportAll := CtrlIsEnabled(sourceCtx, control_export_all) | ||||||
| 	// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
 | 	// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
 | ||||||
| 	// Export variables
 | 	// Export variables
 | ||||||
| 	for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { | 	for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ type Functor interface { | |||||||
| type ExprFuncParam interface { | type ExprFuncParam interface { | ||||||
| 	Name() string | 	Name() string | ||||||
| 	Type() string | 	Type() string | ||||||
|  | 	IsDefault() bool | ||||||
| 	IsOptional() bool | 	IsOptional() bool | ||||||
| 	IsRepeat() bool | 	IsRepeat() bool | ||||||
| 	DefaultValue() any | 	DefaultValue() any | ||||||
| @ -36,6 +37,8 @@ type ExprFunc interface { | |||||||
| type ExprContext interface { | type ExprContext interface { | ||||||
| 	Clone() ExprContext | 	Clone() ExprContext | ||||||
| 	Merge(ctx ExprContext) | 	Merge(ctx ExprContext) | ||||||
|  | 	SetParent(ctx ExprContext) | ||||||
|  | 	GetParent() (ctx ExprContext) | ||||||
| 	GetVar(varName string) (value any, exists bool) | 	GetVar(varName string) (value any, exists bool) | ||||||
| 	SetVar(varName string, value any) | 	SetVar(varName string, value any) | ||||||
| 	UnsafeSetVar(varName string, value any) | 	UnsafeSetVar(varName string, value any) | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								control.go
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								control.go
									
									
									
									
									
								
							| @ -4,13 +4,13 @@ | |||||||
| // control.go
 | // control.go
 | ||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| import "strings" |  | ||||||
| 
 |  | ||||||
| // Preset control variables
 | // Preset control variables
 | ||||||
| const ( | const ( | ||||||
| 	ControlLastResult   = "last" | 	ControlPreset        = "_preset" | ||||||
| 	ControlBoolShortcut = "_bool_shortcut" | 	ControlLastResult    = "last" | ||||||
| 	ControlImportPath   = "_import_path" | 	ControlBoolShortcut  = "_bool_shortcut" | ||||||
|  | 	ControlSearchPath    = "_search_path" | ||||||
|  | 	ControlParentContext = "_parent_context" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Other control variables
 | // Other control variables
 | ||||||
| @ -20,43 +20,14 @@ const ( | |||||||
| 
 | 
 | ||||||
| // Initial values
 | // Initial values
 | ||||||
| const ( | const ( | ||||||
| 	init_import_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr" | 	init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func initDefaultVars(ctx ExprContext) { | func initDefaultVars(ctx ExprContext) { | ||||||
|  | 	if _, exists := ctx.GetVar(ControlPreset); exists { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.SetVar(ControlPreset, true) | ||||||
| 	ctx.SetVar(ControlBoolShortcut, true) | 	ctx.SetVar(ControlBoolShortcut, true) | ||||||
| 	ctx.SetVar(ControlImportPath, init_import_path) | 	ctx.SetVar(ControlSearchPath, init_search_path) | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func enable(ctx ExprContext, name string) { |  | ||||||
| 	if strings.HasPrefix(name, "_") { |  | ||||||
| 		ctx.SetVar(name, true) |  | ||||||
| 	} else { |  | ||||||
| 		ctx.SetVar("_"+name, true) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func disable(ctx ExprContext, name string) { |  | ||||||
| 	if strings.HasPrefix(name, "_") { |  | ||||||
| 		ctx.SetVar(name, false) |  | ||||||
| 	} else { |  | ||||||
| 		ctx.SetVar("_"+name, false) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isEnabled(ctx ExprContext, name string) (status bool) { |  | ||||||
| 	if v, exists := ctx.GetVar(name); exists { |  | ||||||
| 		if b, ok := v.(bool); ok { |  | ||||||
| 			status = b |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func getControlString(ctx ExprContext, name string) (s string, exists bool) { |  | ||||||
| 	var v any |  | ||||||
| 	if v, exists = ctx.GetVar(name); exists { |  | ||||||
| 		s, exists = v.(string) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -212,7 +212,7 @@ func anyToFract(v any) (f *FractionType, err error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if f == nil { | 	if f == nil { | ||||||
| 		err = errExpectedGot("fract", typeFraction, v) | 		err = errExpectedGot("fract", TypeFraction, v) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										32
									
								
								func-base.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								func-base.go
									
									
									
									
									
								
							| @ -159,25 +159,25 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err | |||||||
| 
 | 
 | ||||||
| func ImportBuiltinsFuncs(ctx ExprContext) { | func ImportBuiltinsFuncs(ctx ExprContext) { | ||||||
| 	anyParams := []ExprFuncParam{ | 	anyParams := []ExprFuncParam{ | ||||||
| 		newFuncParam(paramValue), | 		NewFuncParam(paramValue), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams) | 	ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams) | ||||||
| 	ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams) | 	ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams) | ||||||
| 	ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams) | 	ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams) | ||||||
| 	ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{ | 	ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramValue), | 		NewFuncParam(paramValue), | ||||||
| 		newFuncParamFlagDef("denominator", pfOptional, 1), | 		NewFuncParamFlagDef("denominator", PfDefault, 1), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -23,11 +23,11 @@ func printLnFunc(ctx ExprContext, name string, args []any) (result any, err erro | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ImportFmtFuncs(ctx ExprContext) { | func ImportFmtFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("print", newGolangFunctor(printFunc), typeInt, []ExprFuncParam{ | 	ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{ | ||||||
| 		newFuncParamFlag(paramItem, pfRepeat), | 		NewFuncParamFlag(paramItem, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("println", newGolangFunctor(printLnFunc), typeInt, []ExprFuncParam{ | 	ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{ | ||||||
| 		newFuncParamFlag(paramItem, pfRepeat), | 		NewFuncParamFlag(paramItem, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,100 +5,25 @@ | |||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ENV_EXPR_PATH = "EXPR_PATH" |  | ||||||
| 
 |  | ||||||
| func importFunc(ctx ExprContext, name string, args []any) (result any, err error) { | func importFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	return importGeneral(ctx, name, args) | 	return importGeneral(ctx, name, args) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) { | func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	enable(ctx, control_export_all) | 	CtrlEnable(ctx, control_export_all) | ||||||
| 	return importGeneral(ctx, name, args) | 	return importGeneral(ctx, name, args) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) { | func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	var dirList []string | 	dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH) | ||||||
| 
 |  | ||||||
| 	dirList = addEnvImportDirs(dirList) |  | ||||||
| 	dirList = addPresetImportDirs(ctx, dirList) |  | ||||||
| 	result, err = doImport(ctx, name, dirList, NewArrayIterator(args)) | 	result, err = doImport(ctx, name, dirList, NewArrayIterator(args)) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { |  | ||||||
| 	if !(IsString(paramValue) /*|| isList(paramValue)*/) { |  | ||||||
| 		err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func addEnvImportDirs(dirList []string) []string { |  | ||||||
| 	if dirSpec, exists := os.LookupEnv(ENV_EXPR_PATH); exists { |  | ||||||
| 		dirs := strings.Split(dirSpec, ":") |  | ||||||
| 		if dirList == nil { |  | ||||||
| 			dirList = dirs |  | ||||||
| 		} else { |  | ||||||
| 			dirList = append(dirList, dirs...) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return dirList |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func addPresetImportDirs(ctx ExprContext, dirList []string) []string { |  | ||||||
| 	if dirSpec, exists := getControlString(ctx, ControlImportPath); exists { |  | ||||||
| 		dirs := strings.Split(dirSpec, ":") |  | ||||||
| 		if dirList == nil { |  | ||||||
| 			dirList = dirs |  | ||||||
| 		} else { |  | ||||||
| 			dirList = append(dirList, dirs...) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return dirList |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isFile(filePath string) bool { |  | ||||||
| 	info, err := os.Stat(filePath) |  | ||||||
| 	return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func searchAmongPath(filename string, dirList []string) (filePath string) { |  | ||||||
| 	for _, dir := range dirList { |  | ||||||
| 		if fullPath := path.Join(dir, filename); isFile(fullPath) { |  | ||||||
| 			filePath = fullPath |  | ||||||
| 			break |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func isPathRelative(filePath string) bool { |  | ||||||
| 	unixPath := filepath.ToSlash(filePath) |  | ||||||
| 	return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../") |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func makeFilepath(filename string, dirList []string) (filePath string, err error) { |  | ||||||
| 	if path.IsAbs(filename) || isPathRelative(filename) { |  | ||||||
| 		if isFile(filename) { |  | ||||||
| 			filePath = filename |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		filePath = searchAmongPath(filename, dirList) |  | ||||||
| 	} |  | ||||||
| 	if len(filePath) == 0 { |  | ||||||
| 		err = fmt.Errorf("source file %q not found", filename) |  | ||||||
| 	} |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) { | func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) { | ||||||
| 	var v any | 	var v any | ||||||
| 	var sourceFilepath string | 	var sourceFilepath string | ||||||
| @ -115,9 +40,9 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu | |||||||
| 			defer file.Close() | 			defer file.Close() | ||||||
| 			var expr *ast | 			var expr *ast | ||||||
| 			scanner := NewScanner(file, DefaultTranslations()) | 			scanner := NewScanner(file, DefaultTranslations()) | ||||||
| 			parser := NewParser(ctx) | 			parser := NewParser() | ||||||
| 			if expr, err = parser.parseGeneral(scanner, true, true); err == nil { | 			if expr, err = parser.parseGeneral(scanner, true, true); err == nil { | ||||||
| 				result, err = expr.eval(ctx, false) | 				result, err = expr.Eval(ctx) | ||||||
| 			} | 			} | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				break | 				break | ||||||
| @ -137,11 +62,11 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ImportImportFuncs(ctx ExprContext) { | func ImportImportFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{ | 	ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{ | ||||||
| 		newFuncParamFlag(paramFilepath, pfRepeat), | 		NewFuncParamFlag(paramFilepath, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{ | 	ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{ | ||||||
| 		newFuncParamFlag(paramFilepath, pfRepeat), | 		NewFuncParamFlag(paramFilepath, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -167,12 +167,12 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ImportMathFuncs(ctx ExprContext) { | func ImportMathFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{ | 	ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{ | ||||||
| 		newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(0)), | 		NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, int64(0)), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{ | 	ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{ | ||||||
| 		newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, int64(1)), | 		NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, int64(1)), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								func-os.go
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								func-os.go
									
									
									
									
									
								
							| @ -187,25 +187,25 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ImportOsFuncs(ctx ExprContext) { | func ImportOsFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{ | 	ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramFilepath), | 		NewFuncParam(paramFilepath), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{ | 	ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramFilepath), | 		NewFuncParam(paramFilepath), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{ | 	ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramFilepath), | 		NewFuncParam(paramFilepath), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{ | 	ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{ | ||||||
| 		newFuncParam(typeHandle), | 		NewFuncParam(TypeHandle), | ||||||
| 		newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""), | 		NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{ | 	ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{ | ||||||
| 		newFuncParam(typeHandle), | 		NewFuncParam(TypeHandle), | ||||||
| 		newFuncParamFlagDef("limitCh", pfOptional, "\n"), | 		NewFuncParamFlagDef("limitCh", PfDefault, "\n"), | ||||||
| 	}) | 	}) | ||||||
| 	ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{ | 	ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{ | ||||||
| 		newFuncParam(typeHandle), | 		NewFuncParam(TypeHandle), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -21,7 +21,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) | |||||||
| 		if s, ok := v.(string); ok { | 		if s, ok := v.(string); ok { | ||||||
| 			sb.WriteString(s) | 			sb.WriteString(s) | ||||||
| 		} else { | 		} else { | ||||||
| 			err = errExpectedGot(funcName, typeString, v) | 			err = errExpectedGot(funcName, TypeString, v) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @ -51,7 +51,7 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro | |||||||
| 			result, err = doJoinStr(name, sep, NewArrayIterator(args[1:])) | 			result, err = doJoinStr(name, sep, NewArrayIterator(args[1:])) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		err = errWrongParamType(name, paramSeparator, typeString, args[0]) | 		err = errWrongParamType(name, paramSeparator, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @ -63,7 +63,7 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error | |||||||
| 	var ok bool | 	var ok bool | ||||||
| 
 | 
 | ||||||
| 	if source, ok = args[0].(string); !ok { | 	if source, ok = args[0].(string); !ok { | ||||||
| 		return nil, errWrongParamType(name, paramSource, typeString, args[0]) | 		return nil, errWrongParamType(name, paramSource, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if start, err = toInt(args[1], name+"()"); err != nil { | 	if start, err = toInt(args[1], name+"()"); err != nil { | ||||||
| @ -91,7 +91,7 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro | |||||||
| 	var ok bool | 	var ok bool | ||||||
| 
 | 
 | ||||||
| 	if source, ok = args[0].(string); !ok { | 	if source, ok = args[0].(string); !ok { | ||||||
| 		return nil, errWrongParamType(name, paramSource, typeString, args[0]) | 		return nil, errWrongParamType(name, paramSource, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 	result = strings.TrimSpace(source) | 	result = strings.TrimSpace(source) | ||||||
| 	return | 	return | ||||||
| @ -104,7 +104,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er | |||||||
| 	result = false | 	result = false | ||||||
| 
 | 
 | ||||||
| 	if source, ok = args[0].(string); !ok { | 	if source, ok = args[0].(string); !ok { | ||||||
| 		return result, errWrongParamType(name, paramSource, typeString, args[0]) | 		return result, errWrongParamType(name, paramSource, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 	for i, targetSpec := range args[1:] { | 	for i, targetSpec := range args[1:] { | ||||||
| 		if target, ok := targetSpec.(string); ok { | 		if target, ok := targetSpec.(string); ok { | ||||||
| @ -127,7 +127,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err | |||||||
| 	result = false | 	result = false | ||||||
| 
 | 
 | ||||||
| 	if source, ok = args[0].(string); !ok { | 	if source, ok = args[0].(string); !ok { | ||||||
| 		return result, errWrongParamType(name, paramSource, typeString, args[0]) | 		return result, errWrongParamType(name, paramSource, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 	for i, targetSpec := range args[1:] { | 	for i, targetSpec := range args[1:] { | ||||||
| 		if target, ok := targetSpec.(string); ok { | 		if target, ok := targetSpec.(string); ok { | ||||||
| @ -150,7 +150,7 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err | |||||||
| 	var ok bool | 	var ok bool | ||||||
| 
 | 
 | ||||||
| 	if source, ok = args[0].(string); !ok { | 	if source, ok = args[0].(string); !ok { | ||||||
| 		return result, errWrongParamType(name, paramSource, typeString, args[0]) | 		return result, errWrongParamType(name, paramSource, TypeString, args[0]) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if sep, ok = args[1].(string); !ok { | 	if sep, ok = args[1].(string); !ok { | ||||||
| @ -182,37 +182,37 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err | |||||||
| 
 | 
 | ||||||
| // Import above functions in the context
 | // Import above functions in the context
 | ||||||
| func ImportStringFuncs(ctx ExprContext) { | func ImportStringFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{ | 	ctx.RegisterFunc("joinStr", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSeparator), | 		NewFuncParam(paramSeparator), | ||||||
| 		newFuncParamFlag(paramItem, pfRepeat), | 		NewFuncParamFlag(paramItem, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{ | 	ctx.RegisterFunc("subStr", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSource), | 		NewFuncParam(paramSource), | ||||||
| 		newFuncParamFlagDef(paramStart, pfOptional, int64(0)), | 		NewFuncParamFlagDef(paramStart, PfDefault, int64(0)), | ||||||
| 		newFuncParamFlagDef(paramCount, pfOptional, int64(-1)), | 		NewFuncParamFlagDef(paramCount, PfDefault, int64(-1)), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{ | 	ctx.RegisterFunc("splitStr", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSource), | 		NewFuncParam(paramSource), | ||||||
| 		newFuncParamFlagDef(paramSeparator, pfOptional, ""), | 		NewFuncParamFlagDef(paramSeparator, PfDefault, ""), | ||||||
| 		newFuncParamFlagDef(paramCount, pfOptional, int64(-1)), | 		NewFuncParamFlagDef(paramCount, PfDefault, int64(-1)), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{ | 	ctx.RegisterFunc("trimStr", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSource), | 		NewFuncParam(paramSource), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{ | 	ctx.RegisterFunc("startsWithStr", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSource), | 		NewFuncParam(paramSource), | ||||||
| 		newFuncParam(paramPrefix), | 		NewFuncParam(paramPrefix), | ||||||
| 		newFuncParamFlag("other "+paramPrefix, pfRepeat), | 		NewFuncParamFlag("other "+paramPrefix, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{ | 	ctx.RegisterFunc("endsWithStr", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{ | ||||||
| 		newFuncParam(paramSource), | 		NewFuncParam(paramSource), | ||||||
| 		newFuncParam(paramSuffix), | 		NewFuncParam(paramSuffix), | ||||||
| 		newFuncParamFlag("other "+paramSuffix, pfRepeat), | 		NewFuncParamFlag("other "+paramSuffix, PfRepeat), | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								function.go
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								function.go
									
									
									
									
									
								
							| @ -48,7 +48,7 @@ type golangFunctor struct { | |||||||
| 	f FuncTemplate | 	f FuncTemplate | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newGolangFunctor(f FuncTemplate) *golangFunctor { | func NewGolangFunctor(f FuncTemplate) *golangFunctor { | ||||||
| 	return &golangFunctor{f: f} | 	return &golangFunctor{f: f} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -83,7 +83,7 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re | |||||||
| 			if funcArg, ok := arg.(Functor); ok { | 			if funcArg, ok := arg.(Functor); ok { | ||||||
| 				// ctx.RegisterFunc(p, functor, 0, -1)
 | 				// ctx.RegisterFunc(p, functor, 0, -1)
 | ||||||
| 				paramSpecs := funcArg.GetParams() | 				paramSpecs := funcArg.GetParams() | ||||||
| 				ctx.RegisterFunc(p.Name(), funcArg, typeAny, paramSpecs) | 				ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs) | ||||||
| 			} else { | 			} else { | ||||||
| 				ctx.UnsafeSetVar(p.Name(), arg) | 				ctx.UnsafeSetVar(p.Name(), arg) | ||||||
| 			} | 			} | ||||||
| @ -91,7 +91,7 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re | |||||||
| 			ctx.UnsafeSetVar(p.Name(), nil) | 			ctx.UnsafeSetVar(p.Name(), nil) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	result, err = functor.expr.eval(ctx, false) | 	result, err = functor.expr.Eval(ctx) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -99,8 +99,9 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re | |||||||
| type paramFlags uint16 | type paramFlags uint16 | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	pfOptional paramFlags = 1 << iota | 	PfDefault paramFlags = 1 << iota | ||||||
| 	pfRepeat | 	PfOptional | ||||||
|  | 	PfRepeat | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type funcParamInfo struct { | type funcParamInfo struct { | ||||||
| @ -109,15 +110,15 @@ type funcParamInfo struct { | |||||||
| 	defaultValue any | 	defaultValue any | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newFuncParam(name string) ExprFuncParam { | func NewFuncParam(name string) ExprFuncParam { | ||||||
| 	return &funcParamInfo{name: name} | 	return &funcParamInfo{name: name} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam { | func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam { | ||||||
| 	return &funcParamInfo{name: name, flags: flags} | 	return &funcParamInfo{name: name, flags: flags} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo { | func NewFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo { | ||||||
| 	return &funcParamInfo{name: name, flags: flags, defaultValue: defValue} | 	return &funcParamInfo{name: name, flags: flags, defaultValue: defValue} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -126,15 +127,19 @@ func (param *funcParamInfo) Name() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (param *funcParamInfo) Type() string { | func (param *funcParamInfo) Type() string { | ||||||
| 	return "any" | 	return TypeAny | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (param *funcParamInfo) IsDefault() bool { | ||||||
|  | 	return (param.flags & PfDefault) != 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (param *funcParamInfo) IsOptional() bool { | func (param *funcParamInfo) IsOptional() bool { | ||||||
| 	return (param.flags & pfOptional) != 0 | 	return (param.flags & PfOptional) != 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (param *funcParamInfo) IsRepeat() bool { | func (param *funcParamInfo) IsRepeat() bool { | ||||||
| 	return (param.flags & pfRepeat) != 0 | 	return (param.flags & PfRepeat) != 0 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (param *funcParamInfo) DefaultValue() any { | func (param *funcParamInfo) DefaultValue() any { | ||||||
| @ -159,7 +164,7 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF | |||||||
| 			if maxArgs == -1 { | 			if maxArgs == -1 { | ||||||
| 				return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) | 				return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) | ||||||
| 			} | 			} | ||||||
| 			if p.IsOptional() { | 			if p.IsDefault() || p.IsOptional() { | ||||||
| 				maxArgs++ | 				maxArgs++ | ||||||
| 			} else if maxArgs == minArgs { | 			} else if maxArgs == minArgs { | ||||||
| 				minArgs++ | 				minArgs++ | ||||||
| @ -207,7 +212,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string { | |||||||
| 			} | 			} | ||||||
| 			sb.WriteString(p.Name()) | 			sb.WriteString(p.Name()) | ||||||
| 
 | 
 | ||||||
| 			if p.IsOptional() { | 			if p.IsDefault() { | ||||||
| 				sb.WriteByte('=') | 				sb.WriteByte('=') | ||||||
| 				if s, ok := p.DefaultValue().(string); ok { | 				if s, ok := p.DefaultValue().(string); ok { | ||||||
| 					sb.WriteByte('"') | 					sb.WriteByte('"') | ||||||
| @ -226,7 +231,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string { | |||||||
| 	if len(info.returnType) > 0 { | 	if len(info.returnType) > 0 { | ||||||
| 		sb.WriteString(info.returnType) | 		sb.WriteString(info.returnType) | ||||||
| 	} else { | 	} else { | ||||||
| 		sb.WriteString(typeAny) | 		sb.WriteString(TypeAny) | ||||||
| 	} | 	} | ||||||
| 	sb.WriteString(" {<body>}") | 	sb.WriteString(" {<body>}") | ||||||
| 	return sb.String() | 	return sb.String() | ||||||
|  | |||||||
| @ -4,7 +4,10 @@ | |||||||
| // global-context.go
 | // global-context.go
 | ||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| import "path/filepath" | import ( | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| var globalCtx *SimpleStore | var globalCtx *SimpleStore | ||||||
| 
 | 
 | ||||||
| @ -63,7 +66,78 @@ func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, owne | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func GlobalCtrlSet(name string, newValue any) (currentValue any) { | ||||||
|  | 	if !strings.HasPrefix(name, "_") { | ||||||
|  | 		name = "_" + name | ||||||
|  | 	} | ||||||
|  | 	currentValue, _ = globalCtx.GetVar(name) | ||||||
|  | 
 | ||||||
|  | 	globalCtx.SetVar(name, newValue) | ||||||
|  | 	return currentValue | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GlobalCtrlGet(name string) (currentValue any) { | ||||||
|  | 	if !strings.HasPrefix(name, "_") { | ||||||
|  | 		name = "_" + name | ||||||
|  | 	} | ||||||
|  | 	currentValue, _ = globalCtx.GetVar(name) | ||||||
|  | 	return currentValue | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) { | ||||||
|  | 	if !strings.HasPrefix(name, "_") { | ||||||
|  | 		name = "_" + name | ||||||
|  | 	} | ||||||
|  | 	if v, exists := ctx.GetVar(name); exists && IsBool(v) { | ||||||
|  | 		currentStatus, _ = v.(bool) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.SetVar(name, true) | ||||||
|  | 	return currentStatus | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) { | ||||||
|  | 	if !strings.HasPrefix(name, "_") { | ||||||
|  | 		name = "_" + name | ||||||
|  | 	} | ||||||
|  | 	if v, exists := ctx.GetVar(name); exists && IsBool(v) { | ||||||
|  | 		currentStatus, _ = v.(bool) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.SetVar(name, false) | ||||||
|  | 	return currentStatus | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func CtrlIsEnabled(ctx ExprContext, name string) (status bool) { | ||||||
|  | 	var v any | ||||||
|  | 	var exists bool | ||||||
|  | 
 | ||||||
|  | 	if !strings.HasPrefix(name, "_") { | ||||||
|  | 		name = "_" + name | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if v, exists = ctx.GetVar(name); !exists { | ||||||
|  | 		v, exists = globalCtx.GetVar(name) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if exists { | ||||||
|  | 		if b, ok := v.(bool); ok { | ||||||
|  | 			status = b | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getControlString(name string) (s string, exists bool) { | ||||||
|  | 	var v any | ||||||
|  | 	if v, exists = globalCtx.GetVar(name); exists { | ||||||
|  | 		s, exists = v.(string) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func init() { | func init() { | ||||||
| 	globalCtx = NewSimpleStore() | 	globalCtx = NewSimpleStore() | ||||||
|  | 	initDefaultVars(globalCtx) | ||||||
| 	ImportBuiltinsFuncs(globalCtx) | 	ImportBuiltinsFuncs(globalCtx) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								helpers.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								helpers.go
									
									
									
									
									
								
							| @ -16,10 +16,10 @@ func EvalString(ctx ExprContext, source string) (result any, err error) { | |||||||
| 
 | 
 | ||||||
| 	r := strings.NewReader(source) | 	r := strings.NewReader(source) | ||||||
| 	scanner := NewScanner(r, DefaultTranslations()) | 	scanner := NewScanner(r, DefaultTranslations()) | ||||||
| 	parser := NewParser(ctx) | 	parser := NewParser() | ||||||
| 
 | 
 | ||||||
| 	if tree, err = parser.Parse(scanner); err == nil { | 	if tree, err = parser.Parse(scanner); err == nil { | ||||||
| 		result, err = tree.eval(ctx, true) | 		result, err = tree.Eval(ctx) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @ -38,10 +38,10 @@ func EvalStringV(source string, args []Arg) (result any, err error) { | |||||||
| 	for _, arg := range args { | 	for _, arg := range args { | ||||||
| 		if isFunc(arg.Value) { | 		if isFunc(arg.Value) { | ||||||
| 			if f, ok := arg.Value.(FuncTemplate); ok { | 			if f, ok := arg.Value.(FuncTemplate); ok { | ||||||
| 				functor := newGolangFunctor(f) | 				functor := NewGolangFunctor(f) | ||||||
| 				// ctx.RegisterFunc(arg.Name, functor, 0, -1)
 | 				// ctx.RegisterFunc(arg.Name, functor, 0, -1)
 | ||||||
| 				ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{ | 				ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{ | ||||||
| 					newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0), | 					NewFuncParamFlagDef(paramValue, PfDefault|PfRepeat, 0), | ||||||
| 				}) | 				}) | ||||||
| 			} else { | 			} else { | ||||||
| 				err = fmt.Errorf("invalid function specification: %q", arg.Name) | 				err = fmt.Errorf("invalid function specification: %q", arg.Name) | ||||||
| @ -68,7 +68,7 @@ func EvalStringV(source string, args []Arg) (result any, err error) { | |||||||
| func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) { | func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) { | ||||||
| 	var tree *ast | 	var tree *ast | ||||||
| 	scanner := NewScanner(r, DefaultTranslations()) | 	scanner := NewScanner(r, DefaultTranslations()) | ||||||
| 	parser := NewParser(ctx) | 	parser := NewParser() | ||||||
| 
 | 
 | ||||||
| 	if tree, err = parser.Parse(scanner); err == nil { | 	if tree, err = parser.Parse(scanner); err == nil { | ||||||
| 		result, err = tree.Eval(ctx) | 		result, err = tree.Eval(ctx) | ||||||
|  | |||||||
							
								
								
									
										104
									
								
								import-utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								import-utils.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // import-utils.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	ENV_EXPR_SOURCE_PATH = "EXPR_PATH" | ||||||
|  | 	ENV_EXPR_PLUGIN_PATH = "EXPR_PLUGIN_PATH" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { | ||||||
|  | 	if !(IsString(paramValue) /*|| isList(paramValue)*/) { | ||||||
|  | 		err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, typeName(paramValue)) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func addSourceEnvImportDirs(varName string, dirList []string) []string { | ||||||
|  | 	return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func addPluginEnvImportDirs(varName string, dirList []string) []string { | ||||||
|  | 	return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func addEnvImportDirs(envVarName string, dirList []string) []string { | ||||||
|  | 	if dirSpec, exists := os.LookupEnv(envVarName); exists { | ||||||
|  | 		dirs := strings.Split(dirSpec, ":") | ||||||
|  | 		if dirList == nil { | ||||||
|  | 			dirList = dirs | ||||||
|  | 		} else { | ||||||
|  | 			dirList = append(dirList, dirs...) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return dirList | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func addSearchDirs(endingPath string, dirList []string) []string { | ||||||
|  | 	if dirSpec, exists := getControlString(ControlSearchPath); exists { | ||||||
|  | 		dirs := strings.Split(dirSpec, ":") | ||||||
|  | 		if dirList == nil { | ||||||
|  | 			dirList = dirs | ||||||
|  | 		} else { | ||||||
|  | 			if len(endingPath) > 0 { | ||||||
|  | 				for _, d := range dirs { | ||||||
|  | 					dirList = append(dirList, path.Join(d, endingPath)) | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				dirList = append(dirList, dirs...) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return dirList | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func buildSearchDirList(endingPath, envVarName string) (dirList []string) { | ||||||
|  | 	dirList = addEnvImportDirs(envVarName, dirList) | ||||||
|  | 	dirList = addSearchDirs(endingPath, dirList) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isFile(filePath string) bool { | ||||||
|  | 	info, err := os.Stat(filePath) | ||||||
|  | 	return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func searchAmongPath(filename string, dirList []string) (filePath string) { | ||||||
|  | 	for _, dir := range dirList { | ||||||
|  | 		if fullPath := path.Join(dir, filename); isFile(fullPath) { | ||||||
|  | 			filePath = fullPath | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isPathRelative(filePath string) bool { | ||||||
|  | 	unixPath := filepath.ToSlash(filePath) | ||||||
|  | 	return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func makeFilepath(filename string, dirList []string) (filePath string, err error) { | ||||||
|  | 	if path.IsAbs(filename) || isPathRelative(filename) { | ||||||
|  | 		if isFile(filename) { | ||||||
|  | 			filePath = filename | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		filePath = searchAmongPath(filename, dirList) | ||||||
|  | 	} | ||||||
|  | 	if len(filePath) == 0 { | ||||||
|  | 		err = fmt.Errorf("file %q not found", filename) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								list-type.go
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								list-type.go
									
									
									
									
									
								
							| @ -20,6 +20,10 @@ func newListA(listAny ...any) (list *ListType) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newList(listAny []any) (list *ListType) { | func newList(listAny []any) (list *ListType) { | ||||||
|  | 	return NewList(listAny) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewList(listAny []any) (list *ListType) { | ||||||
| 	if listAny != nil { | 	if listAny != nil { | ||||||
| 		ls := make(ListType, len(listAny)) | 		ls := make(ListType, len(listAny)) | ||||||
| 		for i, item := range listAny { | 		for i, item := range listAny { | ||||||
| @ -30,6 +34,23 @@ func newList(listAny []any) (list *ListType) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func MakeList(length, capacity int) (list *ListType) { | ||||||
|  | 	if capacity < length { | ||||||
|  | 		capacity = length | ||||||
|  | 	} | ||||||
|  | 	ls := make(ListType, length, capacity) | ||||||
|  | 	list = &ls | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ListFromStrings(stringList []string) (list *ListType) { | ||||||
|  | 	list = MakeList(len(stringList), 0) | ||||||
|  | 	for i, s := range stringList { | ||||||
|  | 		(*list)[i] = s | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (ls *ListType) ToString(opt FmtOpt) (s string) { | func (ls *ListType) ToString(opt FmtOpt) (s string) { | ||||||
| 	var sb strings.Builder | 	var sb strings.Builder | ||||||
| 	sb.WriteByte('[') | 	sb.WriteByte('[') | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro | |||||||
| 		} | 		} | ||||||
| 		for i, p := range info.Params() { | 		for i, p := range info.Params() { | ||||||
| 			if i >= passedCount { | 			if i >= passedCount { | ||||||
| 				if !p.IsOptional() { | 				if !p.IsDefault() { | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 				*varParams = append(*varParams, p.DefaultValue()) | 				*varParams = append(*varParams, p.DefaultValue()) | ||||||
| @ -50,6 +50,7 @@ func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err erro | |||||||
| 
 | 
 | ||||||
| func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | ||||||
| 	ctx := cloneContext(parentCtx) | 	ctx := cloneContext(parentCtx) | ||||||
|  | 	ctx.SetParent(parentCtx) | ||||||
| 	name, _ := self.tk.Value.(string) | 	name, _ := self.tk.Value.(string) | ||||||
| 	params := make([]any, len(self.children), len(self.children)+5) | 	params := make([]any, len(self.children), len(self.children)+5) | ||||||
| 	for i, tree := range self.children { | 	for i, tree := range self.children { | ||||||
| @ -91,12 +92,12 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 			var defValue any | 			var defValue any | ||||||
| 			flags := paramFlags(0) | 			flags := paramFlags(0) | ||||||
| 			if len(param.children) > 0 { | 			if len(param.children) > 0 { | ||||||
| 				flags |= pfOptional | 				flags |= PfDefault | ||||||
| 				if defValue, err = param.children[0].compute(ctx); err != nil { | 				if defValue, err = param.children[0].compute(ctx); err != nil { | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			info := newFuncParamFlagDef(param.source(), flags, defValue) | 			info := NewFuncParamFlagDef(param.source(), flags, defValue) | ||||||
| 			paramList = append(paramList, info) | 			paramList = append(paramList, info) | ||||||
| 		} | 		} | ||||||
| 		v = newExprFunctor(expr, paramList, ctx) | 		v = newExprFunctor(expr, paramList, ctx) | ||||||
|  | |||||||
| @ -81,7 +81,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 			} else if funcDef, ok := functor.(*exprFunctor); ok { | 			} else if funcDef, ok := functor.(*exprFunctor); ok { | ||||||
| 				paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) | 				paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) | ||||||
| 
 | 
 | ||||||
| 				ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs) | 				ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs) | ||||||
| 			} else { | 			} else { | ||||||
| 				err = self.Errorf("unknown function %s()", rightChild.source()) | 				err = self.Errorf("unknown function %s()", rightChild.source()) | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ func newAndTerm(tk *Token) (inst *term) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalAnd(ctx ExprContext, self *term) (v any, err error) { | func evalAnd(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	if isEnabled(ctx, ControlBoolShortcut) { | 	if CtrlIsEnabled(ctx, ControlBoolShortcut) { | ||||||
| 		v, err = evalAndWithShortcut(ctx, self) | 		v, err = evalAndWithShortcut(ctx, self) | ||||||
| 	} else { | 	} else { | ||||||
| 		v, err = evalAndWithoutShortcut(ctx, self) | 		v, err = evalAndWithoutShortcut(ctx, self) | ||||||
| @ -117,7 +117,7 @@ func newOrTerm(tk *Token) (inst *term) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalOr(ctx ExprContext, self *term) (v any, err error) { | func evalOr(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	if isEnabled(ctx, ControlBoolShortcut) { | 	if CtrlIsEnabled(ctx, ControlBoolShortcut) { | ||||||
| 		v, err = evalOrWithShortcut(ctx, self) | 		v, err = evalOrWithShortcut(ctx, self) | ||||||
| 	} else { | 	} else { | ||||||
| 		v, err = evalOrWithoutShortcut(ctx, self) | 		v, err = evalOrWithoutShortcut(ctx, self) | ||||||
|  | |||||||
| @ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 				if ImportInContext(module) { | 				if ImportInContext(module) { | ||||||
| 					count++ | 					count++ | ||||||
| 				} else { | 				} else { | ||||||
| 					err = self.Errorf("unknown module %q", module) | 					err = self.Errorf("unknown builtin module %q", module) | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				err = self.Errorf("expected string at item nr %d, got %T", it.Index()+1, moduleSpec) | 				err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -67,8 +67,8 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 	} else if rightValue, err = self.children[1].compute(ctx); err == nil { | 	} else if rightValue, err = self.children[1].compute(ctx); err == nil { | ||||||
| 		if functor, ok := rightValue.(Functor); ok { | 		if functor, ok := rightValue.(Functor); ok { | ||||||
| 			//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
 | 			//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
 | ||||||
| 			ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{ | 			ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{ | ||||||
| 				newFuncParamFlag(paramValue, pfOptional|pfRepeat), | 				NewFuncParamFlag(paramValue, PfDefault|PfRepeat), | ||||||
| 			}) | 			}) | ||||||
| 		} else { | 		} else { | ||||||
| 			v = rightValue | 			v = rightValue | ||||||
|  | |||||||
| @ -34,7 +34,8 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 		if formatter, ok := sourceCtx.(Formatter); ok { | 		if formatter, ok := sourceCtx.(Formatter); ok { | ||||||
| 			v = formatter.ToString(0) | 			v = formatter.ToString(0) | ||||||
| 		} else { | 		} else { | ||||||
| 			keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) | 			// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
 | ||||||
|  | 			keys := sourceCtx.EnumVars(nil) | ||||||
| 			d := make(map[string]any) | 			d := make(map[string]any) | ||||||
| 			for _, key := range keys { | 			for _, key := range keys { | ||||||
| 				d[key], _ = sourceCtx.GetVar(key) | 				d[key], _ = sourceCtx.GetVar(key) | ||||||
|  | |||||||
| @ -17,7 +17,7 @@ func newExportAllTerm(tk *Token) (inst *term) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalExportAll(ctx ExprContext, self *term) (v any, err error) { | func evalExportAll(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	enable(ctx, control_export_all) | 	CtrlEnable(ctx, control_export_all) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										102
									
								
								operator-plugin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								operator-plugin.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // operator-plugin.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"plugin" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | //-------- plugin term
 | ||||||
|  | 
 | ||||||
|  | func newPluginTerm(tk *Token) (inst *term) { | ||||||
|  | 	return &term{ | ||||||
|  | 		tk:       *tk, | ||||||
|  | 		children: make([]*term, 0, 1), | ||||||
|  | 		position: posPrefix, | ||||||
|  | 		priority: priSign, | ||||||
|  | 		evalFunc: evalPlugin, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func importPlugin(ctx ExprContext, dirList []string, name string) (err error) { | ||||||
|  | 	var filePath string | ||||||
|  | 	var p *plugin.Plugin | ||||||
|  | 	var sym plugin.Symbol | ||||||
|  | 	var moduleName string | ||||||
|  | 	var importFunc func(ctx ExprContext) | ||||||
|  | 	var ok bool | ||||||
|  | 
 | ||||||
|  | 	decoratedName := fmt.Sprintf("expr-%s-plugin.so", name) | ||||||
|  | 
 | ||||||
|  | 	if filePath, err = makeFilepath(decoratedName, dirList); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if p, err = plugin.Open(filePath); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if sym, err = p.Lookup("MODULE_NAME"); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if moduleName = *sym.(*string); moduleName == "" { | ||||||
|  | 		err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if sym, err = p.Lookup("ImportFuncs"); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if importFunc, ok = sym.(func(ExprContext)); !ok { | ||||||
|  | 		err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	registerPlugin(moduleName, p) | ||||||
|  | 	importFunc(globalCtx) | ||||||
|  | 
 | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func evalPlugin(ctx ExprContext, self *term) (v any, err error) { | ||||||
|  | 	var childValue any | ||||||
|  | 	var moduleSpec any | ||||||
|  | 
 | ||||||
|  | 	if childValue, err = self.evalPrefix(ctx); err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) | ||||||
|  | 	count := 0 | ||||||
|  | 	it := NewAnyIterator(childValue) | ||||||
|  | 	for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { | ||||||
|  | 		if module, ok := moduleSpec.(string); ok { | ||||||
|  | 			if err = importPlugin(ctx, dirList, module); err != nil { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 			count++ | ||||||
|  | 		} else { | ||||||
|  | 			err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if err == io.EOF { | ||||||
|  | 		err = nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err == nil { | ||||||
|  | 		v = int64(count) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // init
 | ||||||
|  | func init() { | ||||||
|  | 	registerTermConstructor(SymKwPlugin, newPluginTerm) | ||||||
|  | } | ||||||
| @ -12,7 +12,7 @@ type intPair struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *intPair) TypeName() string { | func (p *intPair) TypeName() string { | ||||||
| 	return typePair | 	return TypePair | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *intPair) ToString(opt FmtOpt) string { | func (p *intPair) ToString(opt FmtOpt) string { | ||||||
|  | |||||||
| @ -19,17 +19,17 @@ func newSelectorTerm(tk *Token) (inst *term) { | |||||||
| func trySelectorCase(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) | 	caseData, _ := caseSel.(*selectorCase) | ||||||
| 	if caseData.filterList == nil { | 	if caseData.filterList == nil { | ||||||
| 		selectedValue, err = caseData.caseExpr.eval(ctx, false) | 		selectedValue, err = caseData.caseExpr.Eval(ctx) | ||||||
| 		match = true | 		match = true | ||||||
| 	} else if filterList, ok := caseData.filterList.value().([]*term); ok { | 	} else if filterList, ok := caseData.filterList.value().([]*term); ok { | ||||||
| 		if len(filterList) == 0 && exprValue == int64(caseIndex) { | 		if len(filterList) == 0 && exprValue == int64(caseIndex) { | ||||||
| 			selectedValue, err = caseData.caseExpr.eval(ctx, false) | 			selectedValue, err = caseData.caseExpr.Eval(ctx) | ||||||
| 			match = true | 			match = true | ||||||
| 		} else { | 		} else { | ||||||
| 			var caseValue any | 			var caseValue any | ||||||
| 			for _, caseTerm := range filterList { | 			for _, caseTerm := range filterList { | ||||||
| 				if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue { | 				if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue { | ||||||
| 					selectedValue, err = caseData.caseExpr.eval(ctx, false) | 					selectedValue, err = caseData.caseExpr.Eval(ctx) | ||||||
| 					match = true | 					match = true | ||||||
| 					break | 					break | ||||||
| 				} | 				} | ||||||
|  | |||||||
| @ -11,13 +11,10 @@ import ( | |||||||
| //-------- parser
 | //-------- parser
 | ||||||
| 
 | 
 | ||||||
| type parser struct { | type parser struct { | ||||||
| 	ctx ExprContext |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewParser(ctx ExprContext) (p *parser) { | func NewParser() (p *parser) { | ||||||
| 	p = &parser{ | 	p = &parser{} | ||||||
| 		ctx: ctx, |  | ||||||
| 	} |  | ||||||
| 	return p | 	return p | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								plugins.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								plugins.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // plugin.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"plugin" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var pluginRegister map[string]*plugin.Plugin | ||||||
|  | 
 | ||||||
|  | func registerPlugin(name string, p *plugin.Plugin) (err error) { | ||||||
|  | 	if pluginExists(name) { | ||||||
|  | 		err = fmt.Errorf("plugin %q already loaded", name) | ||||||
|  | 	} else { | ||||||
|  | 		pluginRegister[name] = p | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func pluginExists(name string) (exists bool) { | ||||||
|  | 	_, exists = pluginRegister[name] | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	pluginRegister = make(map[string]*plugin.Plugin) | ||||||
|  | } | ||||||
| @ -11,6 +11,7 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type SimpleStore struct { | type SimpleStore struct { | ||||||
|  | 	parent    ExprContext | ||||||
| 	varStore  map[string]any | 	varStore  map[string]any | ||||||
| 	funcStore map[string]ExprFunc | 	funcStore map[string]ExprFunc | ||||||
| } | } | ||||||
| @ -26,6 +27,14 @@ func NewSimpleStore() *SimpleStore { | |||||||
| func filterRefName(name string) bool  { return name[0] != '@' } | func filterRefName(name string) bool  { return name[0] != '@' } | ||||||
| func filterPrivName(name string) bool { return name[0] != '_' } | func filterPrivName(name string) bool { return name[0] != '_' } | ||||||
| 
 | 
 | ||||||
|  | func (ctx *SimpleStore) SetParent(parentCtx ExprContext) { | ||||||
|  | 	ctx.parent = parentCtx | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (ctx *SimpleStore) GetParent() ExprContext { | ||||||
|  | 	return ctx.parent | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (ctx *SimpleStore) Clone() ExprContext { | func (ctx *SimpleStore) Clone() ExprContext { | ||||||
| 	return &SimpleStore{ | 	return &SimpleStore{ | ||||||
| 		varStore:  CloneFilteredMap(ctx.varStore, filterRefName), | 		varStore:  CloneFilteredMap(ctx.varStore, filterRefName), | ||||||
| @ -45,7 +54,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) { | |||||||
| func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) { | func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) { | ||||||
| 	sb.WriteString("vars: {\n") | 	sb.WriteString("vars: {\n") | ||||||
| 	first := true | 	first := true | ||||||
| 	for _, name := range ctx.EnumVars(filterPrivName) { | 	for _, name := range ctx.EnumVars(nil) { | ||||||
| 		if first { | 		if first { | ||||||
| 			first = false | 			first = false | ||||||
| 		} else { | 		} else { | ||||||
|  | |||||||
| @ -101,6 +101,7 @@ const ( | |||||||
| 	SymKwBut | 	SymKwBut | ||||||
| 	SymKwFunc | 	SymKwFunc | ||||||
| 	SymKwBuiltin | 	SymKwBuiltin | ||||||
|  | 	SymKwPlugin | ||||||
| 	SymKwIn | 	SymKwIn | ||||||
| 	SymKwInclude | 	SymKwInclude | ||||||
| 	SymKwNil | 	SymKwNil | ||||||
| @ -113,6 +114,7 @@ func init() { | |||||||
| 	keywords = map[string]Symbol{ | 	keywords = map[string]Symbol{ | ||||||
| 		"AND":     SymKwAnd, | 		"AND":     SymKwAnd, | ||||||
| 		"BUILTIN": SymKwBuiltin, | 		"BUILTIN": SymKwBuiltin, | ||||||
|  | 		"PLUGIN":  SymKwPlugin, | ||||||
| 		"BUT":     SymKwBut, | 		"BUT":     SymKwBut, | ||||||
| 		"FUNC":    SymKwFunc, | 		"FUNC":    SymKwFunc, | ||||||
| 		"IN":      SymKwIn, | 		"IN":      SymKwIn, | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ func TestDictParser(t *testing.T) { | |||||||
| 		ctx.SetVar("var1", int64(123)) | 		ctx.SetVar("var1", int64(123)) | ||||||
| 		ctx.SetVar("var2", "abc") | 		ctx.SetVar("var2", "abc") | ||||||
| 		ImportMathFuncs(ctx) | 		ImportMathFuncs(ctx) | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser() | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFunctionToStringSimple(t *testing.T) { | func TestFunctionToStringSimple(t *testing.T) { | ||||||
| 	source := newGolangFunctor(dummy) | 	source := NewGolangFunctor(dummy) | ||||||
| 	want := "func() {<body>}" | 	want := "func() {<body>}" | ||||||
| 	got := source.ToString(0) | 	got := source.ToString(0) | ||||||
| 	if got != want { | 	if got != want { | ||||||
| @ -52,7 +52,7 @@ func TestFunctionToStringSimple(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFunctionGetFunc(t *testing.T) { | func TestFunctionGetFunc(t *testing.T) { | ||||||
| 	source := newGolangFunctor(dummy) | 	source := NewGolangFunctor(dummy) | ||||||
| 	want := ExprFunc(nil) | 	want := ExprFunc(nil) | ||||||
| 	got := source.GetFunc() | 	got := source.GetFunc() | ||||||
| 	if got != want { | 	if got != want { | ||||||
|  | |||||||
							
								
								
									
										162
									
								
								t_iter-list_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								t_iter-list_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | |||||||
|  | // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | // t_iter-list_test.go
 | ||||||
|  | package expr | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestNewListIterator(t *testing.T) { | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "b" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "b", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewListIterator2(t *testing.T) { | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{3, 1, -1}) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "d" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "d", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewListIterator3(t *testing.T) { | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, -1, 1}) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "b" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "b", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewIterList2(t *testing.T) { | ||||||
|  | 	list := []any{"a", "b", "c", "d"} | ||||||
|  | 	it := NewArrayIterator(list) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "a" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "a", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewIterList3(t *testing.T) { | ||||||
|  | 	list := []any{"a", "b", "c", "d"} | ||||||
|  | 	it := NewAnyIterator(list) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "a" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "a", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewIterList4(t *testing.T) { | ||||||
|  | 	list := any(nil) | ||||||
|  | 	it := NewAnyIterator(list) | ||||||
|  | 	if _, err := it.Next(); err != io.EOF { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewIterList5(t *testing.T) { | ||||||
|  | 	list := "123" | ||||||
|  | 	it := NewAnyIterator(list) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "123" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "123", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestNewIterList6(t *testing.T) { | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it1 := NewAnyIterator(list) | ||||||
|  | 	it := NewAnyIterator(it1) | ||||||
|  | 	if item, err := it.Next(); err != nil { | ||||||
|  | 		t.Errorf("error: %v", err) | ||||||
|  | 	} else if item != "a" { | ||||||
|  | 		t.Errorf("expcted %q, got %q", "a", item) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Next: %v", item) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | func TestNewString(t *testing.T) { | ||||||
|  | 	list := "123" | ||||||
|  | 	it := NewAnyIterator(list) | ||||||
|  | 	if s := it.String(); s != "$(#1)" { | ||||||
|  | 		t.Errorf("expected $(#1), got %s", s) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestHasOperation(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	hasOp := it.HasOperation("reset") | ||||||
|  | 	if !hasOp { | ||||||
|  | 		t.Errorf("HasOperation(reset) must be true, got false") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCallOperationReset(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	if v, err := it.CallOperation("reset", nil); err != nil { | ||||||
|  | 		t.Errorf("Error on CallOperation(reset): %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Reset result: %v", v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCallOperationIndex(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	if v, err := it.CallOperation("index", nil); err != nil { | ||||||
|  | 		t.Errorf("Error on CallOperation(index): %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Index result: %v", v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCallOperationCount(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	if v, err := it.CallOperation("count", nil); err != nil { | ||||||
|  | 		t.Errorf("Error on CallOperation(count): %v", err) | ||||||
|  | 	} else { | ||||||
|  | 		t.Logf("Count result: %v", v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCallOperationUnknown(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	list := newListA("a", "b", "c", "d") | ||||||
|  | 	it := NewListIterator(list, []any{1, 3, 1}) | ||||||
|  | 	if v, err := it.CallOperation("unknown", nil); err == nil { | ||||||
|  | 		t.Errorf("Expected error on CallOperation(unknown), got %v", v) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -6,19 +6,12 @@ package expr | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"strings" |  | ||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestListParser(t *testing.T) { | func TestListParser(t *testing.T) { | ||||||
| 	section := "List" | 	section := "List" | ||||||
| 
 | 
 | ||||||
| 	/*	type inputType struct { |  | ||||||
| 			source     string |  | ||||||
| 			wantResult any |  | ||||||
| 			wantErr    error |  | ||||||
| 		} |  | ||||||
| 	*/ |  | ||||||
| 	inputs := []inputType{ | 	inputs := []inputType{ | ||||||
| 		/*   1 */ {`[]`, newListA(), nil}, | 		/*   1 */ {`[]`, newListA(), nil}, | ||||||
| 		/*   2 */ {`[1,2,3]`, newListA(int64(1), int64(2), int64(3)), nil}, | 		/*   2 */ {`[1,2,3]`, newListA(int64(1), int64(2), int64(3)), nil}, | ||||||
| @ -64,74 +57,4 @@ func TestListParser(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// parserTestSpec(t, section, inputs, 17)
 | 	// parserTestSpec(t, section, inputs, 17)
 | ||||||
| 	parserTest(t, section, inputs) | 	parserTest(t, section, inputs) | ||||||
| 	return |  | ||||||
| 	succeeded := 0 |  | ||||||
| 	failed := 0 |  | ||||||
| 
 |  | ||||||
| 	// inputs1 := []inputType{
 |  | ||||||
| 	// 	/*  7 */ {`add([1,4,3,2])`, int64(10), nil},
 |  | ||||||
| 	// }
 |  | ||||||
| 
 |  | ||||||
| 	for i, input := range inputs { |  | ||||||
| 		var expr *ast |  | ||||||
| 		var gotResult any |  | ||||||
| 		var gotErr error |  | ||||||
| 
 |  | ||||||
| 		ctx := NewSimpleStore() |  | ||||||
| 		ImportMathFuncs(ctx) |  | ||||||
| 		parser := NewParser(ctx) |  | ||||||
| 
 |  | ||||||
| 		logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr) |  | ||||||
| 
 |  | ||||||
| 		r := strings.NewReader(input.source) |  | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) |  | ||||||
| 
 |  | ||||||
| 		good := true |  | ||||||
| 		if expr, gotErr = parser.Parse(scanner); gotErr == nil { |  | ||||||
| 			gotResult, gotErr = expr.Eval(ctx) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) { |  | ||||||
| 			t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) |  | ||||||
| 			good = false |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if gotList, okGot := gotResult.([]any); okGot { |  | ||||||
| 			if wantList, okWant := input.wantResult.([]any); okWant { |  | ||||||
| 				if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) { |  | ||||||
| 					t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) |  | ||||||
| 					good = false |  | ||||||
| 				} else { |  | ||||||
| 					equal := len(gotList) == len(wantList) |  | ||||||
| 					if equal { |  | ||||||
| 						for i, gotItem := range gotList { |  | ||||||
| 							wantItem := wantList[i] |  | ||||||
| 							equal = gotItem == wantItem |  | ||||||
| 							if !equal { |  | ||||||
| 								break |  | ||||||
| 							} |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					if !equal { |  | ||||||
| 						t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult) |  | ||||||
| 						good = false |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if gotErr != input.wantErr { |  | ||||||
| 			if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { |  | ||||||
| 				t.Errorf("%d: %q  -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr) |  | ||||||
| 				good = false |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if good { |  | ||||||
| 			succeeded++ |  | ||||||
| 		} else { |  | ||||||
| 			failed++ |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -188,7 +188,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo | |||||||
| 	var gotErr error | 	var gotErr error | ||||||
| 
 | 
 | ||||||
| 	ctx := NewSimpleStore() | 	ctx := NewSimpleStore() | ||||||
| 	parser := NewParser(ctx) | 	parser := NewParser() | ||||||
| 
 | 
 | ||||||
| 	logTest(t, count, section, input.source, input.wantResult, input.wantErr) | 	logTest(t, count, section, input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user