Compare commits

...

30 Commits

Author SHA1 Message Date
camoroso d215d837f6 Reset() and Clean() have new, simpler signature 2024-09-12 05:44:29 +02:00
camoroso ad3c1e5a60 enhanced and simplified Reset(), Clean() and Next() methods 2024-09-09 15:23:07 +02:00
camoroso d6bf5ee500 common-type-names.go: TypeNil and TyperDict added 2024-09-09 15:16:42 +02:00
camoroso 4b176eb868 Fix function.go: CallFunctionByParams() dit not pass correctly received actual params 2024-08-23 10:29:57 +02:00
camoroso dceb31f542 CallFunction() has been replaced by three new functions:
CallFunctionByTerm(), CallFunctionByArgs() and CallFunctionByParams()
2024-08-02 06:39:33 +02:00
camoroso 075b0b5691 RegisterFunc() also returns the funcInfo object 2024-08-01 00:09:49 +02:00
camoroso 3c51b8d2ee Changed some function names and param types 2024-07-31 09:11:57 +02:00
camoroso a46753f453 Function buildActualParams moved from data-cursor.go ro function.go 2024-07-31 09:08:58 +02:00
camoroso 9070b5c9cc The function parameter model has been modified to support the passing of named parameters 2024-07-28 18:49:08 +02:00
camoroso ab06702e5e operator-post-inc.go: new post int decrement operator '--' 2024-07-24 06:39:35 +02:00
camoroso ffe1fa3aac op-assign expansion now end at ']' and '}' too 2024-07-24 06:37:57 +02:00
camoroso 1a1a475dd8 Added support to op-assign operators like '+='.
This feature is implemented by expansion, not by dedicated operators, e.g. a*=2+x is exapanded as a=a*(2+x)
2024-07-23 15:35:57 +02:00
camoroso 463e3634ba scanner.go: New function UnreadToken().
Currently it only supports one staging level.
2024-07-23 15:32:25 +02:00
camoroso 5f8ca47ef0 term.go: New function Clone() 2024-07-23 15:27:50 +02:00
camoroso 837b887490 token.go: New functions Clone() and IsOneOfA() 2024-07-23 15:27:36 +02:00
camoroso c76e1d3c8e symbol.go: New symbol '*=' 2024-07-23 15:24:54 +02:00
camoroso 315f5b22d3 data-cursor.go: the inizialization of the current item is done in the Next() method.
This allows the application of the filter and map operator to the first item too.
2024-07-23 05:46:37 +02:00
camoroso dfae593e86 operand-iterator.go: removed commented code 2024-07-21 16:35:13 +02:00
camoroso d572f3a129 Iterator: new function Count() 2024-07-21 16:34:52 +02:00
camoroso c461fd138e Iterator defined by data-source now only requires one method: next() 2024-07-21 05:45:22 +02:00
camoroso 6ecbe2feb1 Fixed type (expcted -> expected) 2024-07-21 05:43:55 +02:00
camoroso 80d3c6ec7d parser.go: Fixed an old bug that did not allow the parser to skip comment tokens 2024-07-21 05:37:34 +02:00
camoroso e09806c716 %q replaced by %s in some error messages 2024-07-21 05:33:06 +02:00
camoroso 1a772597cb removed/commented unused code 2024-07-19 17:03:03 +02:00
camoroso 33b3e1fc29 New function for searching and importing plugin 2024-07-19 15:37:00 +02:00
camoroso 4e3f5cfbc6 import-utils.go: Paths are now expanded with respect to env-vars and shell ~ 2024-07-19 15:33:15 +02:00
camoroso e35d4e3f70 utils.go: added function ExpandPath 2024-07-19 15:30:26 +02:00
camoroso b4529499d6 iterator.go: exported some const identifier 2024-07-18 07:27:02 +02:00
camoroso 7745dc24e2 dict-type.go: exported NewDataCursor 2024-07-18 07:25:51 +02:00
camoroso be25385d02 builtin-iterator: changed status variable from 'it_status' to 'status' 2024-07-15 06:59:13 +02:00
53 changed files with 1087 additions and 575 deletions
+6 -6
View File
@@ -45,19 +45,19 @@ func (expr *ast) String() string {
func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens {
if err = expr.addToken(tk); err != nil {
if _, err = expr.addToken(tk); err != nil {
break
}
}
return
}
func (expr *ast) addToken(tk *Token) (err error) {
_, err = expr.addToken2(tk)
return
}
// func (expr *ast) addToken(tk *Token) (err error) {
// _, err = expr.addToken2(tk)
// return
// }
func (expr *ast) addToken2(tk *Token) (t *term, err error) {
func (expr *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil {
err = expr.addTerm(t)
} else {
+18 -19
View File
@@ -12,19 +12,14 @@ type exprFunctor struct {
defCtx ExprContext
}
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
// }
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
return functor.params
}
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
var defCtx ExprContext
if ctx != nil {
// if ctx.GetParent() != nil {
// defCtx = ctx.Clone()
// defCtx.SetParent(ctx)
// } else {
defCtx = ctx
// }
defCtx = ctx
}
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
}
@@ -37,14 +32,10 @@ func (functor *exprFunctor) GetDefinitionContext() ExprContext {
return functor.defCtx
}
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
// if functor.defCtx != nil {
// ctx.Merge(functor.defCtx)
// }
for i, p := range functor.params {
if i < len(args) {
arg := args[i]
func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var missing []string
for _, p := range functor.params {
if arg, exists := args[p.Name()]; exists {
if funcArg, ok := arg.(Functor); ok {
paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
@@ -52,9 +43,17 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
ctx.UnsafeSetVar(p.Name(), arg)
}
} else {
ctx.UnsafeSetVar(p.Name(), nil)
if missing == nil {
missing = make([]string, 0, 1)
}
missing = append(missing, p.Name())
// ctx.UnsafeSetVar(p.Name(), nil)
}
}
result, err = functor.expr.Eval(ctx)
if missing != nil {
err = ErrMissingParams(name, missing)
} else {
result, err = functor.expr.Eval(ctx)
}
return
}
+1 -1
View File
@@ -18,6 +18,6 @@ func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+40 -36
View File
@@ -10,53 +10,57 @@ import (
"strconv"
)
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = args[0] == nil
const (
ParamDenominator = "denominator"
)
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = args[ParamValue] == nil
return
}
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsInteger(args[0])
func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsInteger(args[ParamValue])
return
}
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFloat(args[0])
func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFloat(args[ParamValue])
return
}
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsBool(args[0])
func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsBool(args[ParamValue])
return
}
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsString(args[0])
func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsString(args[ParamValue])
return
}
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFract(args[0])
func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFract(args[ParamValue])
return
}
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsRational(args[0])
func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsRational(args[ParamValue])
return
}
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsList(args[0])
func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsList(args[ParamValue])
return
}
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsDict(args[0])
func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsDict(args[ParamValue])
return
}
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = (v != 0)
case *FractionType:
@@ -73,8 +77,8 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
return
}
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = v
case float64:
@@ -96,8 +100,8 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return
}
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = float64(v)
case float64:
@@ -121,8 +125,8 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return
}
func stringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = strconv.FormatInt(v, 10)
case float64:
@@ -147,18 +151,18 @@ func stringFunc(ctx ExprContext, name string, args []any) (result any, err error
return
}
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
var den int64 = 1
if len(args) > 1 {
var ok bool
if den, ok = args[1].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[1])
} else if den == 0 {
err = ErrFuncDivisionByZero(name)
}
var ok bool
if den, ok = args[ParamDenominator].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[ParamDenominator])
} else if den == 0 {
err = ErrFuncDivisionByZero(name)
}
if err == nil {
result = newFraction(v, den)
}
@@ -205,7 +209,7 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
NewFuncParam(ParamValue),
NewFuncParamFlagDef("denominator", PfDefault, 1),
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
})
}
+14 -8
View File
@@ -21,19 +21,25 @@ func getStdout(ctx ExprContext) io.Writer {
return w
}
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var n int
if n, err = fmt.Fprint(getStdout(ctx), args...); err == nil {
result = int64(n)
func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0
if v, exists := args[ParamItem]; exists && v != nil {
argv := v.([]any)
n, err = fmt.Fprint(getStdout(ctx), argv...)
}
result = int64(n)
return
}
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var n int
if n, err = fmt.Fprintln(getStdout(ctx), args...); err == nil {
result = int64(n)
func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0
if v, exists := args[ParamItem]; exists && v != nil {
argv := v.([]any)
n, err = fmt.Fprintln(getStdout(ctx), argv...)
} else {
n, err = fmt.Fprintln(getStdout(ctx))
}
result = int64(n)
return
}
+7 -4
View File
@@ -9,18 +9,21 @@ import (
"os"
)
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func importFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return importGeneral(ctx, name, args)
}
func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
CtrlEnable(ctx, control_export_all)
return importGeneral(ctx, name, args)
}
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
result, err = doImport(ctx, name, dirList, NewArrayIterator(args))
if v, exists := args[ParamFilepath]; exists && v != nil {
argv := v.([]any)
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
}
return
}
+28 -26
View File
@@ -12,49 +12,51 @@ import (
const (
iterParamOperator = "operator"
iterParamVars = "vars"
iterVarStatus = "status"
)
func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) {
func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Functor, err error) {
var ok bool
if it, ok = args[0].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[0], TypeName(args[0]))
if it, ok = args[ParamIterator].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
return
}
if len(args) > 1 {
if op, ok = args[1].(Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[1], TypeName(args[1]))
return
}
if len(args) > 2 {
var vars *DictType
if vars, ok = args[2].(*DictType); !ok || vars == nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[2], TypeName(args[2]))
return
}
for key, value := range *vars {
var varName string
if varName, ok = key.(string); ok {
localCtx.UnsafeSetVar(varName, value)
}
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
return
}
var vars *DictType
if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], TypeName(args[iterParamVars]))
return
}
if vars != nil {
for key, value := range *vars {
var varName string
if varName, ok = key.(string); ok {
localCtx.UnsafeSetVar(varName, value)
}
}
}
return
}
func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var it Iterator
var ok bool
var op Functor
var v any
var usingDefaultOp = false
var params []any
var params map[string]any
var item any
localCtx := ctx.Clone()
localCtx.UnsafeSetVar("it_status", nil)
localCtx.UnsafeSetVar(iterVarStatus, nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil {
return
@@ -65,12 +67,12 @@ func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
for item, err = it.Next(); err == nil; item, err = it.Next() {
if usingDefaultOp {
params = []any{item}
params = map[string]any{ParamItem: []any{item}}
} else {
params = []any{it.Index(), item}
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
}
if v, err = op.Invoke(localCtx, iterParamOperator, params); err != nil {
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
@@ -84,7 +86,7 @@ func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
err = nil
}
if err == nil {
result, _ = localCtx.GetVar("it_status")
result, _ = localCtx.GetVar(iterVarStatus)
}
return
}
+12 -10
View File
@@ -34,8 +34,8 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
break
}
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
return
}
}
@@ -86,8 +86,9 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
return
}
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1)
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any)
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
return
}
@@ -107,8 +108,8 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
break
}
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
return
}
}
@@ -161,17 +162,18 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
return
}
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1)
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any)
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
return
}
func ImportMathFuncs(ctx ExprContext) {
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
})
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
})
}
+36 -29
View File
@@ -11,6 +11,10 @@ import (
"os"
)
const (
osLimitCh = "limitCh"
)
type osHandle interface {
getFile() *os.File
}
@@ -61,8 +65,8 @@ func errInvalidFileHandle(funcName string, v any) error {
}
}
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func createFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -73,8 +77,8 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return
}
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
@@ -85,8 +89,8 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
return
}
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -97,13 +101,13 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return
}
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
if handle, ok = args[0].(osHandle); !ok {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
@@ -124,18 +128,21 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
return
}
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
if handle, ok = args[0].(osHandle); !ok {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
if w, ok := handle.(*osWriter); ok {
result, err = fmt.Fprint(w.writer, args[1:]...)
if v, exists := args[ParamItem]; exists {
argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...)
}
} else {
invalidFileHandle = handle
}
@@ -147,21 +154,21 @@ func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, er
return
}
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
var limit byte = '\n'
var v string
if s, ok := args[1].(string); ok && len(s) > 0 {
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
limit = s[0]
}
@@ -187,14 +194,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err
return
}
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
@@ -214,34 +221,34 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any,
}
func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParam(ParamHandle),
})
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
NewFuncParam(ParamHandle),
NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""),
})
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
NewFuncParam(ParamHandle),
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
})
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParam(ParamHandle),
})
}
+77 -55
View File
@@ -10,6 +10,10 @@ import (
"strings"
)
const (
strParamOther = "other"
)
// --- Start of function definitions
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
var sb strings.Builder
@@ -32,45 +36,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return
}
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
// if len(args) < 1 {
// return nil, errMissingRequiredParameter(name, paramSeparator)
// }
if sep, ok := args[0].(string); ok {
if len(args) == 1 {
result = ""
} else if len(args) == 2 {
if ls, ok := args[1].(*ListType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if it, ok := args[1].(Iterator); ok {
result, err = doJoinStr(name, sep, it)
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if sep, ok := args[ParamSeparator].(string); ok {
if v, exists := args[ParamItem]; exists {
argv := v.([]any)
if len(argv) == 1 {
if ls, ok := argv[0].(*ListType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if it, ok := argv[0].(Iterator); ok {
result, err = doJoinStr(name, sep, it)
} else if s, ok := argv[0].(string); ok {
result = s
} else {
err = ErrInvalidParameterValue(name, ParamItem, v)
}
} else {
err = ErrInvalidParameterValue(name, ParamParts, args[1])
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
}
} else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
}
} else {
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0])
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
}
return
}
func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var start = 0
var count = -1
var source string
var ok bool
if source, ok = args[0].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
if start, err = ToGoInt(args[1], name+"()"); err != nil {
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil {
return
}
if count, err = ToGoInt(args[2], name+"()"); err != nil {
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
return
}
@@ -86,81 +90,99 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
return
}
func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source string
var ok bool
if source, ok = args[0].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
result = strings.TrimSpace(source)
return
}
func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var source string
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, prefix string
var ok bool
result = false
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) {
result = true
if prefix, ok = args[ParamPrefix].(string); !ok {
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
}
if strings.HasPrefix(source, prefix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break
}
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
break
}
}
return
}
func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var source string
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, suffix string
var ok bool
result = false
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) {
result = true
if suffix, ok = args[ParamSuffix].(string); !ok {
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
}
if strings.HasPrefix(source, suffix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break
}
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
break
}
}
return
}
func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, sep string
var count int = -1
var parts []string
var ok bool
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
if sep, ok = args[1].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1])
if sep, ok = args[ParamSeparator].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
}
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt()
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt()
count = int(count64)
} else {
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2])
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount])
}
if count > 0 {
@@ -206,13 +228,13 @@ func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource),
NewFuncParam(ParamPrefix),
NewFuncParamFlag("other "+ParamPrefix, PfRepeat),
NewFuncParamFlag(strParamOther, PfRepeat),
})
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource),
NewFuncParam(ParamSuffix),
NewFuncParamFlag("other "+ParamSuffix, PfRepeat),
NewFuncParamFlag(strParamOther, PfRepeat),
})
}
+11 -2
View File
@@ -6,8 +6,13 @@ package expr
import (
"fmt"
"strings"
)
func ErrMissingParams(funcName string, missing []string) (err error) {
return fmt.Errorf("%s(): missing params -- %s", funcName, strings.Join(missing, ", "))
}
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
@@ -17,8 +22,8 @@ func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error
return
}
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
return
}
@@ -54,6 +59,10 @@ func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) er
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
}
func ErrUnknownParam(funcName, paramName string) error {
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
}
// --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
+4
View File
@@ -5,8 +5,10 @@
package expr
const (
ParamArgs = "args"
ParamCount = "count"
ParamItem = "item"
ParamIndex = "index"
ParamParts = "parts"
ParamSeparator = "separator"
ParamSource = "source"
@@ -19,6 +21,8 @@ const (
ParamEllipsis = "..."
ParamFilepath = "filepath"
ParamDirpath = "dirpath"
ParamHandle = "handle"
ParamResource = "resource"
ParamIterator = "iterator"
)
+13 -11
View File
@@ -5,16 +5,18 @@
package expr
const (
TypeAny = "any"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeHandle = "handle"
TypeInt = "integer"
TypeItem = "item"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeListOf = "list-of-"
TypeAny = "any"
TypeNil = "nil"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeFileHandle = "file-handle"
TypeInt = "integer"
TypeItem = "item"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeDict = "dict"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)
+199 -111
View File
@@ -5,30 +5,47 @@
package expr
import (
"errors"
"io"
"slices"
)
type dataCursor struct {
ds map[string]Functor
ctx ExprContext
index int
resource any
nextFunc Functor
cleanFunc Functor
resetFunc Functor
currentFunc Functor
ds map[string]Functor
ctx ExprContext
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
// cursorValid bool // true if resource is nil or if clean has not yet been called
index int
count int
current any
lastErr error
resource any
nextFunc Functor
cleanFunc Functor
resetFunc Functor
}
func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{
ds: ds,
index: -1,
ctx: ctx.Clone(),
ds: ds,
initState: true,
// cursorValid: true,
index: -1,
count: 0,
current: nil,
lastErr: nil,
resource: resource,
ctx: ctx.Clone(),
nextFunc: ds[NextName],
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
}
return
}
func (dc *dataCursor) Context() ExprContext {
return dc.ctx
}
func (dc *dataCursor) TypeName() string {
return "DataCursor"
}
@@ -62,7 +79,7 @@ func (dc *dataCursor) String() string {
}
func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == indexName
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
if !exists {
f, ok := dc.ds[name]
exists = ok && isFunctor(f)
@@ -70,148 +87,219 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
return
}
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) {
if name == indexName {
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
if name == IndexName {
value = int64(dc.Index())
} else if name == CleanName {
err = dc.Clean()
} else if name == ResetName {
err = dc.Reset()
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc {
value, err = dc.Clean()
} else if functor == dc.resetFunc {
value, err = dc.Reset()
} else {
ctx := cloneContext(dc.ctx)
value, err = functor.Invoke(ctx, name, []any{})
exportObjects(dc.ctx, ctx)
}
ctx := cloneContext(dc.ctx)
value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx)
} else {
err = errNoOperation(name)
}
return
}
func (dc *dataCursor) Reset() (success bool, err error) {
// func (dc *dataCursor) Reset() (err error) {
// if dc.resetFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
// exportObjects(dc.ctx, ctx)
// dc.index = -1
// dc.count = 0
// dc.initState = true
// dc.current = nil
// dc.lastErr = nil
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(ResetName)
// }
// return
// }
func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil {
dc.index = -1
}
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(resetName)
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
exportObjects(dc.ctx, ctx)
}
success = err == nil
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
return
}
func (dc *dataCursor) Clean() (success bool, err error) {
func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
_, err = dc.cleanFunc.Invoke(ctx, cleanName, []any{dc.resource})
dc.resource = nil
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errors.New("no 'clean' function defined in the data-source")
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
exportObjects(dc.ctx, ctx)
}
success = err == nil
dc.lastErr = io.EOF
return
}
// func (dc *dataCursor) Clean() (err error) {
// if dc.cleanFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
// exportObjects(dc.ctx, ctx)
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(CleanName)
// }
// 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 {
dc.init()
if dc.current != nil {
item = dc.current
} else {
err = io.EOF
}
exportObjects(dc.ctx, ctx)
return
}
// func (dc *dataCursor) _Next() (item any, err error) { // must return io.EOF after the last item
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
// if item == nil {
// err = io.EOF
// } else {
// dc.index++
// }
// }
// // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// exportObjects(dc.ctx, ctx)
// // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
// } else {
// err = errInvalidDataSource()
// }
// return
// }
// func (dc *dataCursor) _filter(item any) (filterdItem any, err error) {
// if filter, ok := dc.ds[filterName]; ok {
// ctx := cloneContext(dc.ctx)
// filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index})
// } else {
// filterdItem = item
// }
// return
// }
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
var v any
var ok bool
ctx := cloneContext(dc.ctx)
if v, err = filter.Invoke(ctx, filterName, []any{item, dc.index}); err == nil && v != nil {
actualParams := bindActualParams(filter, []any{item, dc.index})
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil {
if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
}
return
}
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx)
mappedItem, err = mapper.Invoke(ctx, mapName, []any{item, dc.index});
actualParams := bindActualParams(mapper, []any{item, dc.index})
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
return
}
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
var accepted bool
if dc.resource != nil {
filter := dc.ds[filterName]
mapper := dc.ds[mapName]
func (dc *dataCursor) init() {
if dc.initState {
dc.initState = false
dc.Next()
}
}
for item == nil && err == nil {
ctx := cloneContext(dc.ctx)
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
if item == nil {
err = io.EOF
} else {
dc.index++
if filter != nil {
if accepted, err = dc.checkFilter(filter, item); err != nil || !accepted {
item = nil
}
}
if item != nil && mapper != nil {
item, err = dc.mapItem(mapper, item)
func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
if dc.initState {
dc.init()
} else if err = dc.lastErr; err != nil {
return
}
current = dc.current
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx)
dc.index++
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
accepted := true
if filter != nil {
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil
}
}
if accepted {
dc.count++
}
if item != nil && mapper != nil {
item, dc.lastErr = dc.mapItem(mapper, item)
}
}
exportObjects(dc.ctx, ctx)
}
} else {
err = errInvalidDataSource()
exportObjects(dc.ctx, ctx)
}
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
return
}
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
// if dc.initState {
// dc.init()
// } else if err = dc.lastErr; err != nil {
// return
// }
// current = dc.current
// if dc.resource != nil {
// filter := dc.ds[FilterName]
// mapper := dc.ds[MapName]
// var item any
// for item == nil && dc.lastErr == nil {
// ctx := cloneContext(dc.ctx)
// dc.index++
// actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
// if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
// if item == nil {
// dc.lastErr = io.EOF
// } else {
// accepted := true
// if filter != nil {
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
// item = nil
// }
// }
// if accepted {
// dc.count++
// }
// if item != nil && mapper != nil {
// item, dc.lastErr = dc.mapItem(mapper, item)
// }
// }
// }
// exportObjects(dc.ctx, ctx)
// }
// dc.current = item
// if dc.lastErr != nil {
// dc.index--
// dc.Clean()
// }
// } else {
// dc.lastErr = errInvalidDataSource()
// }
// return
// }
func (dc *dataCursor) Index() int {
return dc.index
return dc.index - 1
}
func (dc *dataCursor) Count() int {
return dc.count
}
+15
View File
@@ -18,7 +18,22 @@ func MakeDict() (dict *DictType) {
return
}
func NewDict(dictAny map[any]any) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func newDict(dictAny map[any]*term) (dict *DictType) {
// TODO Change wi a call to NewDict()
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
+2 -3
View File
@@ -7,7 +7,6 @@ package expr
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
@@ -24,7 +23,7 @@ type ExprContext interface {
DeleteFunc(funcName string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
Call(name string, args map[string]any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
}
+3 -2
View File
@@ -7,7 +7,7 @@ package expr
// ---- Functor interface
type Functor interface {
Typer
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
@@ -32,7 +32,8 @@ type ExprFunc interface {
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ParamSpec(paramName string) ExprFuncParam
ReturnType() string
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
PrepareCall(name string, actualParams map[string]any) (err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
}
+153 -23
View File
@@ -6,11 +6,12 @@ package expr
import (
"fmt"
"strconv"
"strings"
)
// ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
// ---- Common functor definition
type baseFunctor struct {
@@ -137,10 +138,6 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
return info, nil
}
// func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
// return newFuncInfo("unnamed", functor, returnType, params)
// }
func (info *funcInfo) Params() []ExprFuncParam {
return info.formalParams
}
@@ -216,37 +213,133 @@ func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
return
}
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
passedCount := len(*varActualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
for i := passedCount; i < len(info.formalParams); i++ {
p := info.formalParams[i]
if !p.IsDefault() {
for i, tree := range callTerm.children {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.compute(paramCtx); err != nil {
break
}
if paramName, namedParam := getAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, len(callTerm.children)-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
*varActualParams = append(*varActualParams, p.DefaultValue())
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if err == nil {
ctx = info.AllocContext(parentCtx)
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
// ----- Call a function ---
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
var ctx ExprContext
if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
functor := info.Functor()
result, err = functor.Invoke(ctx, name, actualParams)
func getAssignVarName(t *term) (name string, ok bool) {
if ok = t.symbol() == SymEqual; ok {
name = t.children[0].source()
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
actualParams = bindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
@@ -254,3 +347,40 @@ func CallFunction(parentCtx ExprContext, name string, actualParams []any) (resul
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func bindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+14 -1
View File
@@ -55,7 +55,20 @@ func GetLocalFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool)
}
return
}
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
// if len(name) > 0 {
// if item, exists = GetLocalFuncInfo(ctx, name); exists {
// ownerCtx = ctx
// } else if item, exists = globalCtx.GetFuncInfo(name); exists {
// ownerCtx = globalCtx
// }
// }
item, exists, _ = GetFuncInfoAndOwner(ctx, name)
return
}
func GetFuncInfoAndOwner(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
if len(name) > 0 {
if item, exists = GetLocalFuncInfo(ctx, name); exists {
ownerCtx = ctx
+8
View File
@@ -75,7 +75,11 @@ func isFile(filePath string) bool {
}
func searchAmongPath(filename string, dirList []string) (filePath string) {
var err error
for _, dir := range dirList {
if dir, err = ExpandPath(dir); err != nil {
continue
}
if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath
break
@@ -90,6 +94,10 @@ func isPathRelative(filePath string) bool {
}
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if filename, err = ExpandPath(filename); err != nil {
return
}
if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) {
filePath = filename
+13 -10
View File
@@ -12,15 +12,15 @@ import (
// Operator names
const (
initName = "init"
cleanName = "clean"
resetName = "reset"
nextName = "next"
currentName = "current"
indexName = "index"
countName = "count"
filterName = "filter"
mapName = "map"
InitName = "init"
CleanName = "clean"
ResetName = "reset"
NextName = "next"
CurrentName = "current"
IndexName = "index"
CountName = "count"
FilterName = "filter"
MapName = "map"
)
type Iterator interface {
@@ -28,12 +28,15 @@ type Iterator interface {
Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error)
Index() int
Count() int
}
type ExtIterator interface {
Iterator
Reset() error
Clean() error
HasOperation(name string) bool
CallOperation(name string, args []any) (value any, err error)
CallOperation(name string, args map[string]any) (value any, err error)
}
func errNoOperation(name string) error {
+21 -10
View File
@@ -90,21 +90,23 @@ func (it *ListIterator) TypeName() string {
}
func (it *ListIterator) HasOperation(name string) bool {
yes := name == nextName || name == resetName || name == indexName || name == countName || name == currentName
yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
return yes
}
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case nextName:
case NextName:
v, err = it.Next()
case resetName:
v, err = it.Reset()
case indexName:
case ResetName:
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case currentName:
case CurrentName:
v, err = it.Current()
case countName:
case CountName:
v = it.count
default:
err = errNoOperation(name)
@@ -143,7 +145,16 @@ func (it *ListIterator) Index() int {
return it.index
}
func (it *ListIterator) Reset() (bool, error) {
func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (error) {
it.index = it.start - it.step
return true, nil
it.count = 0
return nil
}
func (it *ListIterator) Clean() (error) {
return nil
}
+18 -12
View File
@@ -21,20 +21,26 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
}
// -------- eval func call
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
// name, _ := opTerm.tk.Value.(string)
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
// for i, tree := range opTerm.children {
// var param any
// if param, err = tree.compute(ctx); err != nil {
// break
// }
// params[i] = param
// }
// if err == nil {
// v, err = CallFunction(ctx, name, params)
// }
// return
// }
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := opTerm.tk.Value.(string)
params := make([]any, len(opTerm.children), len(opTerm.children)+5)
for i, tree := range opTerm.children {
var param any
if param, err = tree.compute(ctx); err != nil {
break
}
params[i] = param
}
if err == nil {
v, err = CallFunction(ctx, name, params)
}
v, err = CallFunctionByTerm(ctx, name, opTerm)
return
}
+14 -29
View File
@@ -11,22 +11,6 @@ import (
// -------- iterator term
// func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
// tk.Sym = SymIterator
// children := make([]*term, 0, 1+len(args))
// children = append(children, dsTerm)
// children = append(children, args...)
// return &term{
// tk: *tk,
// parent: nil,
// children: children,
// position: posLeaf,
// priority: priValue,
// evalFunc: evalIterator,
// }
// }
func newIteratorTerm(tk *Token, args []*term) *term {
tk.Sym = SymIterator
return &term{
@@ -66,8 +50,8 @@ func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error)
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{currentName, nextName}
fieldsMask := 0b11
requiredFields := []string{NextName}
fieldsMask := 0b1
foundFields := 0
ds = make(map[string]Functor)
for keyAny, item := range *dictAny {
@@ -88,7 +72,6 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
missingFields = append(missingFields, field)
}
}
// err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
}
}
@@ -108,9 +91,11 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
}
if ds != nil {
dc := newDataCursor(ctx, ds)
if initFunc, exists := ds[initName]; exists && initFunc != nil {
var dc *dataCursor
dcCtx := ctx.Clone()
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any
var resource any
if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
return
@@ -119,18 +104,18 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
args = []any{}
}
initCtx := dc.ctx.Clone()
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
actualParams := bindActualParams(initFunc, args)
initCtx := ctx.Clone()
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
return
}
exportObjects(dc.ctx, initCtx)
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} else {
dc = NewDataCursor(dcCtx, ds, nil)
}
dc.nextFunc = ds[nextName]
dc.currentFunc = ds[currentName]
dc.cleanFunc = ds[cleanName]
dc.resetFunc = ds[resetName]
v = dc
} else if list, ok := firstChildValue.(*ListType); ok {
var args []any
+1 -1
View File
@@ -25,7 +25,7 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
var exists bool
name := opTerm.source()
if v, exists = GetVar(ctx, name); !exists {
if info, exists, _ := GetFuncInfo(ctx, name); exists {
if info, exists := GetFuncInfo(ctx, name); exists {
v = info.Functor()
} else {
err = fmt.Errorf("undefined variable or function %q", name)
+1 -1
View File
@@ -32,7 +32,7 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
opName := indexTerm.source()
if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, []any{})
v, err = unboxedValue.CallOperation(opName, map[string]any{})
} else {
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
v = false
+7 -7
View File
@@ -30,16 +30,16 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
s, _ := childValue.(string)
v = int64(len(s))
} else if IsDict(childValue) {
// m, _ := childValue.(map[any]any)
m, _ := childValue.(*DictType)
v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
count, _ := extIt.CallOperation(countName, nil)
v, _ = ToGoInt(count, "")
} else {
v = int64(it.Index() + 1)
}
v = int64(it.Count())
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
// count, _ := extIt.CallOperation(CountName, nil)
// v, _ = ToGoInt(count, "")
// } else {
// v = int64(it.Index() + 1)
// }
} else {
err = opTerm.errIncompatibleType(childValue)
}
+2 -24
View File
@@ -4,10 +4,6 @@
// operator-plugin.go
package expr
import (
"io"
)
//-------- plugin term
func newPluginTerm(tk *Token) (inst *term) {
@@ -22,31 +18,13 @@ func newPluginTerm(tk *Token) (inst *term) {
func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
var moduleSpec any
var count int
if childValue, err = opTerm.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(dirList, module); err != nil {
break
}
count++
} else {
err = opTerm.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
if err == nil {
if count, err = importPluginFromSearchPath(childValue); err == nil {
v = int64(count)
}
return
+32
View File
@@ -35,7 +35,39 @@ func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
// -------- post decrement term
func newPostDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostDec,
}
}
func evalPostDec(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
/* if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else */if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i-1)
} else {
err = opTerm.errIncompatibleType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm)
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
}
+74 -18
View File
@@ -18,6 +18,12 @@ func NewParser() (p *parser) {
return p
}
func (parser *parser) Next(scanner *scanner) (tk *Token) {
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() {
}
return
}
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10)
itemExpected := false
@@ -57,11 +63,11 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
itemExpected := false
tk := scanner.Previous()
for lastSym != SymClosedRound && lastSym != SymEos {
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) {
param := newTerm(tk)
args = append(args, param)
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.Sym == SymEqual {
var paramExpr *ast
defaultParamsStarted = true
@@ -86,7 +92,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
err = tk.ErrorExpectedGot(")")
}
if err == nil {
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) {
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
} else {
@@ -184,8 +190,8 @@ func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree
return
}
func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
tk := scanner.Next()
func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
tk := parser.Next(scanner)
if tk.Sym == SymError {
err = tk.Error()
return
@@ -194,7 +200,7 @@ func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any,
return
}
if tk.Sym == SymInteger || tk.Sym == SymString {
tkSep := scanner.Next()
tkSep := parser.Next(scanner)
if tkSep.Sym != SymColon {
err = tkSep.ErrorExpectedGot(":")
} else {
@@ -213,7 +219,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
for lastSym != SymClosedBrace && lastSym != SymEos {
var subTree *ast
var key any
if key, err = parser.parseDictKey(scanner, allowVarRef); err != nil {
if key, err = parser.parseDictKey(scanner); err != nil {
break
} else if key == nil {
tk := scanner.Previous()
@@ -251,7 +257,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term
var caseExpr *ast
tk := scanner.Next()
tk := parser.Next(scanner)
startRow := tk.row
startCol := tk.col
if tk.Sym == SymOpenSquare {
@@ -262,7 +268,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
return
}
tk = scanner.Next()
tk = parser.Next(scanner)
startRow = tk.row
startCol = tk.col
} else if !defaultCase {
@@ -299,7 +305,7 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
var caseTerm *term
tk := scanner.makeToken(SymSelector, '?')
if selectorTerm, err = tree.addToken2(tk); err != nil {
if selectorTerm, err = tree.addToken(tk); err != nil {
return
}
@@ -337,13 +343,14 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
var selectorTerm *term = nil
var currentTerm *term = nil
var tk *Token
tree = NewAst()
firstToken := true
// lastSym := SymUnknown
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() {
if tk.Sym == SymComment {
continue
}
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
// if tk.Sym == SymComment {
// continue
// }
if tk.Sym == SymSemiColon {
if allowForest {
@@ -409,7 +416,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
}
case SymEqual:
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
currentTerm, err = tree.addToken2(tk)
currentTerm, err = tree.addToken(tk)
// }
case SymFuncDef:
var funcDefTerm *term
@@ -427,7 +434,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
if tk.source[0] == '@' && !allowVarRef {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
} else {
currentTerm, err = tree.addToken2(tk)
currentTerm, err = tree.addToken(tk)
}
case SymQuestion:
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
@@ -444,14 +451,16 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
}
}
} else {
currentTerm, err = tree.addToken2(tk)
currentTerm, err = tree.addToken(tk)
}
if tk.IsSymbol(SymColon) {
// Colon outside a selector term acts like a separator
firstToken = true
}
case SymPlusEqual, SymMinusEqual, SymStarEqual:
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
default:
currentTerm, err = tree.addToken2(tk)
currentTerm, err = tree.addToken(tk)
}
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
@@ -460,6 +469,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
}
// lastSym = tk.Sym
}
if err == nil {
err = tk.Error()
}
@@ -472,3 +482,49 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
// }
// return
// }
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
var opSym Symbol
var opString string
if tree.root != nil {
switch tk.Sym {
case SymPlusEqual:
opString = "+"
opSym = SymPlus
case SymMinusEqual:
opString = "-"
opSym = SymMinus
case SymStarEqual:
opString = "*"
opSym = SymStar
default:
err = tk.Errorf("unsopported operator %q", tk.source)
return
}
leftExpr := tree.root.Clone()
leftExpr.setParent(nil)
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
t = leftExpr
if err = tree.addTerm(leftExpr); err == nil {
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
var subTree *ast
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
if err = scanner.UnreadToken(); err != nil {
return
}
}
subTree.root.priority = priValue
err = tree.addTerm(newExprTerm(subTree.root))
t = subTree.root
}
}
}
}
} else {
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
}
return
}
+24
View File
@@ -6,6 +6,7 @@ package expr
import (
"fmt"
"io"
"os"
"plugin"
"strings"
@@ -90,6 +91,29 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
return
}
func importPluginFromSearchPath(name any) (count int, err error) {
var moduleSpec any
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count = 0
it := NewAnyIterator(name)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil {
break
}
count++
} else {
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
return
}
func loadModules(dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil {
+25 -7
View File
@@ -16,6 +16,7 @@ import (
type scanner struct {
current *Token
prev *Token
stage *Token
stream *bufio.Reader
row int
column int
@@ -74,6 +75,16 @@ func (scanner *scanner) unreadChar() (err error) {
return
}
func (scanner *scanner) UnreadToken() (err error) {
if scanner.stage == nil {
scanner.stage = scanner.current
scanner.current = scanner.prev
} else {
err = fmt.Errorf("staging already present, currently one level only of staging is allowed")
}
return
}
func (scanner *scanner) lastPos() (r, c int) {
if scanner.prev != nil {
r = scanner.prev.row
@@ -89,7 +100,12 @@ func (scanner *scanner) Previous() *Token {
func (scanner *scanner) Next() (tk *Token) {
scanner.prev = scanner.current
tk = scanner.current
scanner.current = scanner.fetchNextToken()
if scanner.stage != nil {
scanner.current = scanner.stage
scanner.stage = nil
} else {
scanner.current = scanner.fetchNextToken()
}
return tk
}
@@ -124,6 +140,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.moveOn(SymDoubleStar, ch, next)
// } else if next == '/' {
// tk = self.moveOn(SymClosedComment, ch, next)
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymStarEqual, ch, next)
} else {
tk = scanner.makeToken(SymStar, ch)
}
@@ -269,11 +287,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.makeToken(SymDollar, ch)
}
case '(':
// if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else {
tk = scanner.makeToken(SymOpenRound, ch)
// }
// if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else {
tk = scanner.makeToken(SymOpenRound, ch)
// }
case ')':
tk = scanner.makeToken(SymClosedRound, ch)
case '[':
@@ -440,7 +458,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
tk = scanner.makeErrorToken(err)
} else {
var value any
err = scanner.sync(err) // TODO: Check this function
err = scanner.sync(err) // TODO: Check this function
txt := sb.String()
if sym == SymFloat {
value, err = strconv.ParseFloat(txt, 64)
+4 -3
View File
@@ -149,10 +149,11 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
}
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (exprFunc ExprFunc, err error) {
var info *funcInfo
if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
ctx.funcStore[name] = info
exprFunc = info
}
return
}
@@ -179,10 +180,10 @@ func (ctx *SimpleStore) DeleteFunc(funcName string) {
delete(ctx.funcStore, funcName)
}
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
func (ctx *SimpleStore) Call(name string, args map[string]any) (result any, err error) {
if info, exists := GetLocalFuncInfo(ctx, name); exists {
functor := info.Functor()
result, err = functor.Invoke(ctx, name, args)
result, err = functor.InvokeNamed(ctx, name, args)
} else {
err = fmt.Errorf("unknown function %s()", name)
}
+1
View File
@@ -68,6 +68,7 @@ const (
SymDoubleDollar // 57: '$$'
SymDoubleDot // 58: '..'
SymTripleDot // 59: '...'
SymStarEqual // 60: '*='
SymChangeSign
SymUnchangeSign
SymIdentifier
+1 -1
View File
@@ -47,7 +47,7 @@ func TestAddUnknownTokens(t *testing.T) {
wantErr := errors.New(`unexpected token "%"`)
tree := NewAst()
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
if _, gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr)
}
}
+3 -2
View File
@@ -21,7 +21,7 @@ func TestFuncBase(t *testing.T) {
/* 7 */ {`int(3.9)`, int64(3), nil},
/* 8 */ {`int("432")`, int64(432), nil},
/* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
/* 10 */ {`int("432", 4)`, nil, `int(): too much params -- expected 1, got 2`},
/* 10 */ {`int("432", 4)`, nil, `int(): too many params -- expected 1, got 2`},
/* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
/* 12 */ {`isInt(2+1)`, true, nil},
/* 13 */ {`isInt(3.1)`, false, nil},
@@ -43,7 +43,7 @@ func TestFuncBase(t *testing.T) {
/* 29 */ {`dec(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too much params -- expected 1, got 3`},
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
/* 33 */ {`isBool(false)`, true, nil},
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
@@ -61,5 +61,6 @@ func TestFuncBase(t *testing.T) {
t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs)
}
+1 -1
View File
@@ -30,7 +30,7 @@ func TestFmt(t *testing.T) {
text := "ciao mondo"
inputs := []inputType{
/* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
/* 1 */ {fmt.Sprintf(`builtin "fmt"; println("%s")`, text), int64(11), nil},
}
// t.Setenv("EXPR_PATH", ".")
+1 -1
View File
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
t.Setenv("EXPR_PATH", "test-resources")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}
+5 -25
View File
@@ -14,37 +14,17 @@ func TestFuncRun(t *testing.T) {
inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){it_status=it_status+item; true}, {"it_status":0})`, int64(6), nil},
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, `paramter "operator" must be a function, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil},
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
}
//t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}
// func TestFmt(t *testing.T) {
// section := "Builtin-Fmt"
// text := "ciao mondo"
// inputs := []inputType{
// /* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
// }
// // t.Setenv("EXPR_PATH", ".")
// var b bytes.Buffer
// ctx := NewSimpleStore()
// currentStdout := SetCtrl(ctx, ControlStdout, &b)
// runCtxTestSuite(t, ctx, section, inputs)
// SetCtrl(ctx, ControlStdout, currentStdout)
// if b.String() != text+"\n" {
// t.Errorf("println(): Got: %q, Want: %q", b.String(), text+"\n")
// }
// }
+6 -1
View File
@@ -19,10 +19,15 @@ func TestFuncMathArith(t *testing.T) {
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
/* 7 */ {`builtin "math.arith"; a=5; b=2; add(a, b*3)`, int64(11), nil},
/* 8 */ {`builtin "math.arith"; var2="abc"; add(1,2) but var2`, "abc", nil},
/* 9 */ {`builtin "math.arith"; add()`, int64(0), nil},
/* 10 */ {`builtin "math.arith"; mul()`, int64(1), nil},
/* 11 */ {`builtin "math.arith"; add([1,2,3])`, int64(6), nil},
/* 12 */ {`builtin "math.arith"; mul([2,2,3])`, int64(12), nil},
/* 13 */ {`builtin "math.arith"; mul(2,2,3)`, int64(12), nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs)
}
+1 -1
View File
@@ -30,6 +30,6 @@ func TestFuncOs(t *testing.T) {
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs)
}
+6 -4
View File
@@ -42,6 +42,8 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
failed := 0
for i, input := range inputs {
// fmt.Printf("%3d: %s\n", i+1, input.source)
good := doTest(t, ctx, section, &input, i+1)
if good {
succeeded++
@@ -91,13 +93,13 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ {
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
good = false
}
if gotErr != wantErr {
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
good = false
}
}
@@ -106,8 +108,8 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
if wantErr == nil {
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
t.Logf("[+]%s nr %3d -- `%s` --> %v", section, n, source, wantResult)
} else {
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
}
}
+7 -2
View File
@@ -22,7 +22,7 @@ func TestDictParser(t *testing.T) {
inputs := []inputType{
/* 1 */ {`{}`, map[any]any{}, nil},
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
@@ -32,7 +32,12 @@ func TestDictParser(t *testing.T) {
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
}
/* 12 */ {`m={
"a":1,
//"b":2,
"c":3
}`, map[any]any{"a": 1, "c": 3}, nil},
}
succeeded := 0
failed := 0
+15 -4
View File
@@ -17,10 +17,21 @@ func TestExpr(t *testing.T) {
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {`
/* 6 */ {`a=3; a+=1`, int64(4), nil},
/* 7 */ {`a=3; a-=1`, int64(2), nil},
/* 8 */ {`a=3; a*=2`, int64(6), nil},
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
/* 17 */ {`
ds={
"init":func(end){@end=end; @current=0 but true},
"current":func(){current},
"init":func(@end){@current=0 but true},
//"current":func(){current},
"next":func(){
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
}
@@ -32,6 +43,6 @@ func TestExpr(t *testing.T) {
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 3)
// runTestSuiteSpec(t, section, inputs, 17)
runTestSuite(t, section, inputs)
}
+11 -7
View File
@@ -24,24 +24,29 @@ func TestFuncs(t *testing.T) {
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too many params -- expected 0, got 1`},
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, `[1:24] expected "function-param-value", got ")"`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
/* 20 */ {`f=func(a,b){a*2+b}; f(1,10)`, int64(12), nil},
/* 21 */ {`f=func(a,b){a*2+b}; f(a=2,b=1)`, int64(5), nil},
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 20)
//runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs)
}
func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
func dummy(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return
}
@@ -54,7 +59,6 @@ func TestFunctionToStringSimple(t *testing.T) {
}
}
func TestFunctionGetFunc(t *testing.T) {
source := NewGolangFunctor(dummy)
want := ExprFunc(nil)
+40 -39
View File
@@ -5,50 +5,51 @@
package expr
import (
"fmt"
"testing"
)
func subtract(ctx ExprContext, name string, args []any) (result any, err error) {
if len(args) != 2 {
err = fmt.Errorf("%s(): requires exactly two arguments", name)
return
}
x, xok := args[0].(int64)
y, yok := args[1].(int64)
if xok && yok {
result = x - y
} else {
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
}
return
}
// TODO The new function param model does not allow this kind of test
// ------------------------------------------------------------------
// func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
// if len(args) != 2 {
// err = fmt.Errorf("%s(): requires exactly two arguments", name)
// return
// }
// x, xok := args[0].(int64)
// y, yok := args[1].(int64)
// if xok && yok {
// result = x - y
// } else {
// err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
// }
// return
// }
func TestEvalStringA(t *testing.T) {
// func TestEvalStringA(t *testing.T) {
source := `a + b * subtract(4,2)`
args := []Arg{
{"a", uint8(1)},
{"b", int8(2)},
{"subtract", FuncTemplate(subtract)},
// force coverage
{"a16", uint16(1)},
{"b16", int16(2)},
{"a32", uint32(1)},
{"b32", int32(2)},
{"a64", uint64(1)},
{"b64", int64(2)},
{"f32", float32(1.0)},
{"f64", float64(1.0)},
}
// source := `a + b * subtract(4,2)`
// args := []Arg{
// {"a", uint8(1)},
// {"b", int8(2)},
// {"subtract", FuncTemplate2(subtract)},
// // force coverage
// {"a16", uint16(1)},
// {"b16", int16(2)},
// {"a32", uint32(1)},
// {"b32", int32(2)},
// {"a64", uint64(1)},
// {"b64", int64(2)},
// {"f32", float32(1.0)},
// {"f64", float64(1.0)},
// }
wantResult := int64(5)
gotResult, gotErr := EvalStringA(source, args...)
if value, ok := gotResult.(int64); ok && value != wantResult {
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
t.Errorf("Error: %v", gotErr)
}
}
// wantResult := int64(5)
// gotResult, gotErr := EvalStringA(source, args...)
// if value, ok := gotResult.(int64); ok && value != wantResult {
// t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
// t.Errorf("Error: %v", gotErr)
// }
// }
func TestEvalString(t *testing.T) {
@@ -68,7 +69,7 @@ func TestEvalString(t *testing.T) {
// force coverage
ctx.GetFuncInfo("dummy")
ctx.Call("dummy", []any{})
ctx.Call("dummy", map[string]any{})
source := `a + b * f`
+7 -7
View File
@@ -15,7 +15,7 @@ func TestNewListIterator(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
t.Errorf("expected %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
@@ -27,7 +27,7 @@ func TestNewListIterator2(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "d" {
t.Errorf("expcted %q, got %q", "d", item)
t.Errorf("expected %q, got %q", "d", item)
} else {
t.Logf("Next: %v", item)
}
@@ -39,7 +39,7 @@ func TestNewListIterator3(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
t.Errorf("expected %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
@@ -51,7 +51,7 @@ func TestNewIterList2(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
@@ -63,7 +63,7 @@ func TestNewIterList3(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
@@ -83,7 +83,7 @@ func TestNewIterList5(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "123" {
t.Errorf("expcted %q, got %q", "123", item)
t.Errorf("expected %q, got %q", "123", item)
} else {
t.Logf("Next: %v", item)
}
@@ -96,7 +96,7 @@ func TestNewIterList6(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
+5 -3
View File
@@ -11,13 +11,13 @@ func TestIteratorParser(t *testing.T) {
inputs := []inputType{
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, nil, nil},
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
@@ -25,8 +25,10 @@ func TestIteratorParser(t *testing.T) {
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
}
//runTestSuiteSpec(t, section, inputs, 4)
//runTestSuiteSpec(t, section, inputs, 18)
runTestSuite(t, section, inputs)
}
+3 -3
View File
@@ -128,8 +128,8 @@ func TestGeneralParser(t *testing.T) {
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
/* 115 */ {`nil`, nil, nil},
/* 116 */ {`null`, nil, `undefined variable or function "null"`},
/* 117 */ {`{"key"}`, nil, `[1:8] expected ":", got "}"`},
/* 118 */ {`{"key":}`, nil, `[1:9] expected "dictionary-value", got "}"`},
/* 117 */ {`{"key"}`, nil, "[1:8] expected `:`, got `}`"},
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
/* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
@@ -144,6 +144,6 @@ func TestGeneralParser(t *testing.T) {
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 102)
//runTestSuiteSpec(t, section, inputs, 130)
runTestSuite(t, section, inputs)
}
+9 -2
View File
@@ -9,8 +9,15 @@ import (
)
func _TestImportPlugin(t *testing.T) {
if err := importPlugin([]string{"test-resources"}, "json"); err != nil {
t.Errorf("importPlugin() failed: %v", err)
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
gotCount, gotErr := importPluginFromSearchPath("json")
if gotCount != 1 {
t.Errorf("Import count: got=%d, want=1", gotCount)
}
if gotErr != nil {
t.Errorf("importPlugin() failed: %v", gotErr)
}
}
+19
View File
@@ -53,6 +53,25 @@ type term struct {
evalFunc evalFuncType
}
func (s *term) Clone() (d *term) {
var children []*term
if s.children != nil {
children = make([]*term, len(s.children))
for i, c := range s.children {
children[i] = c.Clone()
}
}
d = &term{
tk: *s.tk.Clone(),
parent: s.parent,
children: children,
position: s.position,
priority: s.priority,
evalFunc: s.evalFunc,
}
return
}
func (term *term) String() string {
var sb strings.Builder
term.toString(&sb)
+11 -3
View File
@@ -28,7 +28,7 @@ func (tk *Token) DevString() string {
func (tk *Token) String() string {
if tk.Value != nil {
if s, ok := tk.Value.(string); ok {
return fmt.Sprintf("%q", s)
return s //fmt.Sprintf("%q", s)
} else {
return fmt.Sprintf("%v", tk.Value)
}
@@ -51,6 +51,10 @@ func NewErrorToken(row, col int, err error) *Token {
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
}
func (tk *Token) Clone() (c *Token) {
return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
}
func (tk *Token) IsEos() bool {
return tk.Sym == SymEos
}
@@ -67,6 +71,10 @@ func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsOneOfA(termSymbols ...Symbol) bool {
return slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsSymbol(sym Symbol) bool {
return tk.Sym == sym
}
@@ -91,11 +99,11 @@ func (tk *Token) Errors(msg string) (err error) {
}
func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, tk)
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, tk)
return
}
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, got)
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
return
}
+38
View File
@@ -6,7 +6,11 @@ package expr
import (
"fmt"
"os"
"os/user"
"path"
"reflect"
"strings"
)
func IsString(v any) (ok bool) {
@@ -221,3 +225,37 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
}
return result
}
func ExpandPath(sourcePath string) (expandedPath string, err error) {
for expandedPath = os.ExpandEnv(sourcePath); expandedPath != sourcePath; expandedPath = os.ExpandEnv(sourcePath) {
sourcePath = expandedPath
}
if strings.HasPrefix(sourcePath, "~") {
var home, userName, remainder string
slashPos := strings.IndexRune(sourcePath, '/')
if slashPos > 0 {
userName = sourcePath[1:slashPos]
remainder = sourcePath[slashPos:]
} else {
userName = sourcePath[1:]
}
if len(userName) == 0 {
home, err = os.UserHomeDir()
if err != nil {
return
}
} else {
var userInfo *user.User
userInfo, err = user.Lookup(userName)
if err != nil {
return
}
home = userInfo.HomeDir
}
expandedPath = path.Join(home, remainder)
}
return
}