Compare commits

..

10 Commits

37 changed files with 781 additions and 477 deletions
+16 -17
View File
@@ -12,19 +12,14 @@ type exprFunctor struct {
defCtx ExprContext defCtx ExprContext
} }
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor { func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
// return &exprFunctor{expr: e, params: params, defCtx: ctx} return functor.params
// } }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor { func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
var defCtx ExprContext var defCtx ExprContext
if ctx != nil { 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} return &exprFunctor{expr: e, params: params, defCtx: defCtx}
} }
@@ -37,14 +32,10 @@ func (functor *exprFunctor) GetDefinitionContext() ExprContext {
return functor.defCtx return functor.defCtx
} }
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
// if functor.defCtx != nil { var missing []string
// ctx.Merge(functor.defCtx) for _, p := range functor.params {
// } if arg, exists := args[p.Name()]; exists {
for i, p := range functor.params {
if i < len(args) {
arg := args[i]
if funcArg, ok := arg.(Functor); ok { if funcArg, ok := arg.(Functor); ok {
paramSpecs := funcArg.GetParams() paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs) 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) ctx.UnsafeSetVar(p.Name(), arg)
} }
} else { } else {
ctx.UnsafeSetVar(p.Name(), nil) if missing == nil {
missing = make([]string, 0, 1)
}
missing = append(missing, p.Name())
// ctx.UnsafeSetVar(p.Name(), nil)
} }
} }
if missing != nil {
err = ErrMissingParams(name, missing)
} else {
result, err = functor.expr.Eval(ctx) result, err = functor.expr.Eval(ctx)
}
return return
} }
+1 -1
View File
@@ -18,6 +18,6 @@ func (functor *golangFunctor) TypeName() string {
return "GoFunctor" 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) return functor.f(ctx, name, args)
} }
+37 -33
View File
@@ -10,53 +10,57 @@ import (
"strconv" "strconv"
) )
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) { const (
result = args[0] == nil ParamDenominator = "denominator"
)
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = args[ParamValue] == nil
return return
} }
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsInteger(args[0]) result = IsInteger(args[ParamValue])
return return
} }
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFloat(args[0]) result = IsFloat(args[ParamValue])
return return
} }
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsBool(args[0]) result = IsBool(args[ParamValue])
return return
} }
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsString(args[0]) result = IsString(args[ParamValue])
return return
} }
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFract(args[0]) result = IsFract(args[ParamValue])
return return
} }
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsRational(args[0]) result = IsRational(args[ParamValue])
return return
} }
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsList(args[0]) result = IsList(args[ParamValue])
return return
} }
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsDict(args[0]) result = IsDict(args[ParamValue])
return return
} }
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) { func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[ParamValue].(type) {
case int64: case int64:
result = (v != 0) result = (v != 0)
case *FractionType: case *FractionType:
@@ -73,8 +77,8 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
return return
} }
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[ParamValue].(type) {
case int64: case int64:
result = v result = v
case float64: case float64:
@@ -96,8 +100,8 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return return
} }
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) { func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[ParamValue].(type) {
case int64: case int64:
result = float64(v) result = float64(v)
case float64: case float64:
@@ -121,8 +125,8 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return return
} }
func stringFunc(ctx ExprContext, name string, args []any) (result any, err error) { func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[ParamValue].(type) {
case int64: case int64:
result = strconv.FormatInt(v, 10) result = strconv.FormatInt(v, 10)
case float64: case float64:
@@ -147,18 +151,18 @@ func stringFunc(ctx ExprContext, name string, args []any) (result any, err error
return return
} }
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[ParamValue].(type) {
case int64: case int64:
var den int64 = 1 var den int64 = 1
if len(args) > 1 {
var ok bool var ok bool
if den, ok = args[1].(int64); !ok { if den, ok = args[ParamDenominator].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[1]) err = ErrExpectedGot(name, "integer", args[ParamDenominator])
} else if den == 0 { } else if den == 0 {
err = ErrFuncDivisionByZero(name) err = ErrFuncDivisionByZero(name)
} }
}
if err == nil { if err == nil {
result = newFraction(v, den) result = newFraction(v, den)
} }
@@ -205,7 +209,7 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams) ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{ ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
NewFuncParam(ParamValue), 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 return w
} }
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) { func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int var n int = 0
if n, err = fmt.Fprint(getStdout(ctx), args...); err == nil { if v, exists := args[ParamItem]; exists && v != nil {
result = int64(n) argv := v.([]any)
n, err = fmt.Fprint(getStdout(ctx), argv...)
} }
result = int64(n)
return return
} }
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) { func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int var n int = 0
if n, err = fmt.Fprintln(getStdout(ctx), args...); err == nil { if v, exists := args[ParamItem]; exists && v != nil {
result = int64(n) argv := v.([]any)
n, err = fmt.Fprintln(getStdout(ctx), argv...)
} else {
n, err = fmt.Fprintln(getStdout(ctx))
} }
result = int64(n)
return return
} }
+7 -4
View File
@@ -9,18 +9,21 @@ import (
"os" "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) 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) CtrlEnable(ctx, control_export_all)
return importGeneral(ctx, name, args) return importGeneral(ctx, name, args)
} }
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) { func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH) 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 return
} }
+16 -15
View File
@@ -15,25 +15,26 @@ const (
iterVarStatus = "status" 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 var ok bool
if it, ok = args[0].(Iterator); !ok { if it, ok = args[ParamIterator].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[0], TypeName(args[0])) err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
return return
} }
if len(args) > 1 { if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
if op, ok = args[1].(Functor); !ok || op == nil { err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[1], TypeName(args[1]))
return return
} }
if len(args) > 2 {
var vars *DictType var vars *DictType
if vars, ok = args[2].(*DictType); !ok || vars == nil { if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[2], TypeName(args[2])) err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], TypeName(args[iterParamVars]))
return return
} }
if vars != nil {
for key, value := range *vars { for key, value := range *vars {
var varName string var varName string
if varName, ok = key.(string); ok { if varName, ok = key.(string); ok {
@@ -41,17 +42,17 @@ func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, er
} }
} }
} }
}
return 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 it Iterator
var ok bool var ok bool
var op Functor var op Functor
var v any var v any
var usingDefaultOp = false var usingDefaultOp = false
var params []any var params map[string]any
var item any var item any
localCtx := ctx.Clone() localCtx := ctx.Clone()
@@ -66,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() { for item, err = it.Next(); err == nil; item, err = it.Next() {
if usingDefaultOp { if usingDefaultOp {
params = []any{item} params = map[string]any{ParamItem: []any{item}}
} else { } 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 break
} else { } else {
var success bool var success bool
+8 -6
View File
@@ -86,8 +86,9 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) { func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1) argv := args[ParamValue].([]any)
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
@@ -161,17 +162,18 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1) argv := args[ParamValue].([]any)
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
func ImportMathFuncs(ctx ExprContext) { 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)), 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)), NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
}) })
} }
+36 -29
View File
@@ -11,6 +11,10 @@ import (
"os" "os"
) )
const (
osLimitCh = "limitCh"
)
type osHandle interface { type osHandle interface {
getFile() *os.File 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) { func createFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Create(filePath); err == nil { if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} 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 return
} }
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Open(filePath); err == nil { if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)} 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 return
} }
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil { 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)} 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 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 handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[0].(osHandle); !ok { if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[0] invalidFileHandle = args[ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -124,18 +128,21 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
return 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 handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[0].(osHandle); !ok { if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[0] invalidFileHandle = args[ParamHandle]
} }
if handle != nil { if handle != nil {
if w, ok := handle.(*osWriter); ok { 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 { } else {
invalidFileHandle = handle invalidFileHandle = handle
} }
@@ -147,21 +154,21 @@ func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, er
return 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 handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil { if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[0] invalidFileHandle = args[ParamHandle]
} }
if handle != nil { if handle != nil {
if r, ok := handle.(*osReader); ok { if r, ok := handle.(*osReader); ok {
var limit byte = '\n' var limit byte = '\n'
var v string 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] limit = s[0]
} }
@@ -187,14 +194,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err
return 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 handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil { if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[0] invalidFileHandle = args[ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -214,34 +221,34 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any,
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(ParamHandle),
}) })
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(ParamHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""), NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""),
}) })
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(ParamHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"), NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
}) })
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(ParamHandle),
}) })
} }
+64 -42
View File
@@ -10,6 +10,10 @@ import (
"strings" "strings"
) )
const (
strParamOther = "other"
)
// --- Start of function definitions // --- Start of function definitions
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) { func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
var sb strings.Builder var sb strings.Builder
@@ -32,45 +36,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return return
} }
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
// if len(args) < 1 { if sep, ok := args[ParamSeparator].(string); ok {
// return nil, errMissingRequiredParameter(name, paramSeparator) if v, exists := args[ParamItem]; exists {
// } argv := v.([]any)
if sep, ok := args[0].(string); ok { if len(argv) == 1 {
if len(args) == 1 { if ls, ok := argv[0].(*ListType); ok {
result = ""
} else if len(args) == 2 {
if ls, ok := args[1].(*ListType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if it, ok := args[1].(Iterator); ok { } else if it, ok := argv[0].(Iterator); ok {
result, err = doJoinStr(name, sep, it) result, err = doJoinStr(name, sep, it)
} else if s, ok := argv[0].(string); ok {
result = s
} else { } else {
err = ErrInvalidParameterValue(name, ParamParts, args[1]) err = ErrInvalidParameterValue(name, ParamItem, v)
} }
} else { } else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:])) result, err = doJoinStr(name, sep, NewArrayIterator(argv))
}
} }
} else { } else {
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0]) err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
} }
return 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 start = 0
var count = -1 var count = -1
var source string var source string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0]) 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 return
} }
if count, err = ToGoInt(args[2], name+"()"); err != nil { if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
return return
} }
@@ -86,81 +90,99 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
return 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 source string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
} }
result = strings.TrimSpace(source) result = strings.TrimSpace(source)
return return
} }
func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source, prefix string
var ok bool var ok bool
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
} }
for i, targetSpec := range args[1:] {
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 target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) { if strings.HasPrefix(source, target) {
result = true result = true
break break
} }
} else { } else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec) err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break break
} }
} }
}
return return
} }
func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source, suffix string
var ok bool var ok bool
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
} }
for i, targetSpec := range args[1:] {
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 target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) { if strings.HasSuffix(source, target) {
result = true result = true
break break
} }
} else { } else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec) err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break break
} }
} }
}
return 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 source, sep string
var count int = -1 var count int = -1
var parts []string var parts []string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
} }
if sep, ok = args[1].(string); !ok { if sep, ok = args[ParamSeparator].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1]) 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) count = int(count64)
} else { } 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 { if count > 0 {
@@ -206,13 +228,13 @@ func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParam(ParamPrefix), NewFuncParam(ParamPrefix),
NewFuncParamFlag("other "+ParamPrefix, PfRepeat), NewFuncParamFlag(strParamOther, PfRepeat),
}) })
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParam(ParamSuffix), NewFuncParam(ParamSuffix),
NewFuncParamFlag("other "+ParamSuffix, PfRepeat), NewFuncParamFlag(strParamOther, PfRepeat),
}) })
} }
+11 -2
View File
@@ -6,8 +6,13 @@ package expr
import ( import (
"fmt" "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) { func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 { if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount) 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 return
} }
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) { func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount) err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
return 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) 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 // --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error { func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
+4
View File
@@ -5,8 +5,10 @@
package expr package expr
const ( const (
ParamArgs = "args"
ParamCount = "count" ParamCount = "count"
ParamItem = "item" ParamItem = "item"
ParamIndex = "index"
ParamParts = "parts" ParamParts = "parts"
ParamSeparator = "separator" ParamSeparator = "separator"
ParamSource = "source" ParamSource = "source"
@@ -19,6 +21,8 @@ const (
ParamEllipsis = "..." ParamEllipsis = "..."
ParamFilepath = "filepath" ParamFilepath = "filepath"
ParamDirpath = "dirpath" ParamDirpath = "dirpath"
ParamHandle = "handle"
ParamResource = "resource"
ParamIterator = "iterator" ParamIterator = "iterator"
) )
+3 -1
View File
@@ -6,15 +6,17 @@ package expr
const ( const (
TypeAny = "any" TypeAny = "any"
TypeNil = "nil"
TypeBoolean = "boolean" TypeBoolean = "boolean"
TypeFloat = "float" TypeFloat = "float"
TypeFraction = "fraction" TypeFraction = "fraction"
TypeHandle = "handle" TypeFileHandle = "file-handle"
TypeInt = "integer" TypeInt = "integer"
TypeItem = "item" TypeItem = "item"
TypeNumber = "number" TypeNumber = "number"
TypePair = "pair" TypePair = "pair"
TypeString = "string" TypeString = "string"
TypeDict = "dict"
TypeListOf = "list-of-" TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings" TypeListOfStrings = "list-of-strings"
) )
+111 -39
View File
@@ -6,12 +6,14 @@ package expr
import ( import (
"io" "io"
"slices"
) )
type dataCursor struct { type dataCursor struct {
ds map[string]Functor ds map[string]Functor
ctx ExprContext ctx ExprContext
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor) 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 index int
count int count int
current any current any
@@ -26,6 +28,7 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
initState: true, initState: true,
// cursorValid: true,
index: -1, index: -1,
count: 0, count: 0,
current: nil, current: nil,
@@ -36,7 +39,6 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
cleanFunc: ds[CleanName], cleanFunc: ds[CleanName],
resetFunc: ds[ResetName], resetFunc: ds[ResetName],
} }
//dc.Next()
return return
} }
@@ -77,7 +79,7 @@ func (dc *dataCursor) String() string {
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) { func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == IndexName exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
if !exists { if !exists {
f, ok := dc.ds[name] f, ok := dc.ds[name]
exists = ok && isFunctor(f) exists = ok && isFunctor(f)
@@ -85,64 +87,86 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
return return
} }
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
if name == IndexName { if name == IndexName {
value = int64(dc.Index()) 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) { } 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) ctx := cloneContext(dc.ctx)
value, err = functor.Invoke(ctx, name, []any{}) value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
}
} else { } else {
err = errNoOperation(name) err = errNoOperation(name)
} }
return 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.resetFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
_, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource}) actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
}
dc.index = -1 dc.index = -1
dc.count = 0 dc.count = 0
dc.initState = true dc.initState = true
dc.current = nil dc.current = nil
dc.lastErr = nil dc.lastErr = nil
//dc.Next()
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(ResetName)
}
success = err == nil
return return
} }
func (dc *dataCursor) Clean() (success bool, err error) { func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil { if dc.cleanFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
_, err = dc.cleanFunc.Invoke(ctx, CleanName, []any{dc.resource}) actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
// dc.resource = nil _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
} }
} else { dc.lastErr = io.EOF
err = errNoOperation(CleanName)
}
success = err == nil
return 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 func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
dc.init() dc.init()
@@ -158,7 +182,9 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
var v any var v any
var ok bool var ok bool
ctx := cloneContext(dc.ctx) 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 { 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
} }
@@ -168,7 +194,8 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) { func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx) 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 return
} }
@@ -186,14 +213,15 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
return return
} }
current = dc.current current = dc.current
if dc.resource != nil {
filter := dc.ds[FilterName] filter := dc.ds[FilterName]
mapper := dc.ds[MapName] mapper := dc.ds[MapName]
var item any var item any
for item == nil && dc.lastErr == nil { for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
dc.index++ dc.index++
if item, dc.lastErr = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource, dc.index}); dc.lastErr == nil {
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 { if item == nil {
dc.lastErr = io.EOF dc.lastErr = io.EOF
} else { } else {
@@ -218,12 +246,56 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
dc.index-- dc.index--
dc.Clean() dc.Clean()
} }
} else {
dc.lastErr = errInvalidDataSource()
}
return 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 { func (dc *dataCursor) Index() int {
return dc.index - 1 return dc.index - 1
} }
+40 -34
View File
@@ -58,7 +58,7 @@ The expression context is analogous to the stack-frame of other programming lang
Function contexts are created by cloning the calling context. More details on this topic are given later in this document. Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The createt context can be called _function context_. _Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_. Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
@@ -321,7 +321,7 @@ Some arithmetic operators can also be used with strings.
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_ | [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|=== |===
The items of strings can be accessed using the square `[]` operator. The charanters in a string can be accessed using the square `[]` operator.
.Item access syntax .Item access syntax
==== ====
@@ -340,10 +340,10 @@ The items of strings can be accessed using the square `[]` operator.
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ + `>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
[green]`"b"` [green]`"b"`
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ + `>>>` [blue]`s[-1]` [gray]_// char at position -1, the rightmost one_ +
[green]`"d"` [green]`"d"`
`>>>` [blue]`\#s` [gray]_// number of chars_ + `>>>` [blue]`#s` [gray]_// number of chars_ +
[gren]`4` [gren]`4`
`>>>` [blue]`#"abc"` [gray]_// number of chars_ + `>>>` [blue]`#"abc"` [gray]_// number of chars_ +
@@ -369,9 +369,9 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ + | [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ +
[blue]`"b" \<= "b"` -> _true_ [blue]`"b" \<= "b"` -> _true_
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ + | [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ +
[blue]`"a" < "b"` -> _false_ [blue]`"a" > "b"` -> _false_
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ + | [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ +
[blue]`"b" \<= "b"` -> _true_ [blue]`"b" >= "b"` -> _true_
|=== |===
^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections. ^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections.
@@ -388,7 +388,7 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ + | [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ +
[blue]`"a" < "b" AND NOT (2 < 1)` -> _true_ [blue]`"a" < "b" AND NOT (2 < 1)` -> _true_
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers true| [blue]`false or true` -> _true_ + | [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers is true| [blue]`false or true` -> _true_ +
[blue]`"a" == "b" OR (2 == 1)` -> _false_ [blue]`"a" == "b" OR (2 == 1)` -> _false_
|=== |===
@@ -413,7 +413,7 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions.
==== ====
*_list_* = _empty-list_ | _non-empty-list_ + *_list_* = _empty-list_ | _non-empty-list_ +
_empty-list_ = "**[]**" + _empty-list_ = "**[]**" +
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" + _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
==== ====
.Examples .Examples
@@ -459,22 +459,22 @@ Array's items can be accessed using the index `[]` operator.
==== ====
.Items of list .Items of list
`>>>` [blue]`[1,2,3].1` + `>>>` [blue]`[1,2,3][1]` +
[green]`2` [green]`2`
`>>>` [blue]`list=[1,2,3]; list.1` + `>>>` [blue]`list=[1,2,3]; list[1]` +
[green]`2` [green]`2`
`>>>` [blue]`["one","two","three"].1` + `>>>` [blue]`["one","two","three"][1]` +
[green]`two` [green]`two`
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` + `>>>` [blue]`list=["one","two","three"]; list[2-1]` +
[green]`two` [green]`two`
`>>>` [blue]`list.(-1)` + `>>>` [blue]`list[-1]` +
[green]`three` [green]`three`
`>>>` [blue]`list.(10)` + `>>>` [blue]`list[10]` +
[red]`Eval Error: [1:9] index 10 out of bounds` [red]`Eval Error: [1:9] index 10 out of bounds`
`>>>` [blue]`#list` + `>>>` [blue]`#list` +
@@ -497,7 +497,7 @@ Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed
==== ====
*_dict_* = _empty-dict_ | _non-empty-dict_ + *_dict_* = _empty-dict_ | _non-empty-dict_ +
_empty-dict_ = "**{}**" + _empty-dict_ = "**{}**" +
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" + _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value_} "**}**" +
==== ====
@@ -551,7 +551,7 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
[green]`1` [green]`1`
`>>>` [blue]`a_b=1+2` + `>>>` [blue]`a_b=1+2` +
[green]`1+2` [green]`3`
`>>>` [blue]`a_b` + `>>>` [blue]`a_b` +
[green]`3` [green]`3`
@@ -562,7 +562,7 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
`>>>` [blue]`x = 1; y = 2*x` + `>>>` [blue]`x = 1; y = 2*x` +
[green]`2` [green]`2`
`>>>` [blue]`_a=2` + `>>>` [blue]`\_a=2` +
[red]`Parse Error: [1:2] unexpected token "_"` [red]`Parse Error: [1:2] unexpected token "_"`
`>>>` [blue]`1=2` + `>>>` [blue]`1=2` +
@@ -574,12 +574,12 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
=== [blue]`;` operator === [blue]`;` operator
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result. The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
.Mult-expression syntax .Multi-expression syntax
==== ====
*_multi-expression_* = _expression_ {"**;**" _expression_ } *_multi-expression_* = _expression_ {"**;**" _expression_ }
==== ====
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_. An expression that contains [blue]`;` is called a _multi-expression_ and each component expression is called a _sub-expression_.
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions. IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
@@ -589,7 +589,7 @@ TIP: [blue]`;` can be used to set some variables before the final calculation.
`>>>` [blue]`a=1; b=2; c=3; a+b+c` + `>>>` [blue]`a=1; b=2; c=3; a+b+c` +
[green]`6` [green]`6`
The value of each sub-expression is stored in the automatica variable _last_. The value of each sub-expression is stored in the automatic variable _last_.
.Example .Example
`>>>` [blue]`2+3; b=last+10; last` + `>>>` [blue]`2+3; b=last+10; last` +
@@ -600,9 +600,10 @@ The value of each sub-expression is stored in the automatica variable _last_.
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. [blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.
.Examples .Examples
[blue]`5 but 2` + `>>>` [blue]`5 but 2` +
[green]`2` + [green]`2`
[blue]`x=2*3 but x-1` +
`>>>` [blue]`x=2*3 but x-1` +
[green]`5`. [green]`5`.
[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`. [blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`.
@@ -610,17 +611,21 @@ The value of each sub-expression is stored in the automatica variable _last_.
=== Assignment operator [blue]`=` === Assignment operator [blue]`=`
The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_). The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_).
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation. The value on the left side of [blue]`=` must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation.
.Example .Examples
`>>>` [blue]`a=15+1` `>>>` [blue]`a=15+1` +
[green]`16` [green]`16`
`>>>` [blue]`L=[1,2,3]; L[1]=5; L` +
[green]`[1, 5, 3]`
=== Selector operator [blue]`? : ::` === Selector operator [blue]`? : ::`
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages. The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
.Selector literal Syntax .Selector literal Syntax
====
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] + _selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
_selector-case_ = [_match-list_] _case-value_ + _selector-case_ = [_match-list_] _case-value_ +
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" + _match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
@@ -628,6 +633,7 @@ _item_ = _expression_ +
_case-multi-expression_ = "*{*" _multi-expression_ "*}*" + _case-multi-expression_ = "*{*" _multi-expression_ "*}*" +
_multi-expression_ = _expression_ { "*;*" _expression_ } + _multi-expression_ = _expression_ { "*;*" _expression_ } +
_default-multi-expression_ = _multi-expression_ _default-multi-expression_ = _multi-expression_
====
In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation. In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation.
@@ -668,7 +674,7 @@ The [blue]`??` operator do not change the status of the left variable.
The [blue]`?=` assigns the calculated value of the right expression to the left variable. The [blue]`?=` assigns the calculated value of the right expression to the left variable.
.Examples .Examples
`>>>` [blue]`var ?? (1+2)`' + `>>>` [blue]`var ?? (1+2)` +
[green]`3` [green]`3`
`>>>` [blue]`var` + `>>>` [blue]`var` +
@@ -677,7 +683,7 @@ The [blue]`?=` assigns the calculated value of the right expression to the left
`>>>` [blue]`var ?= (1+2)` + `>>>` [blue]`var ?= (1+2)` +
[green]`3` [green]`3`
`>>>` [blue]`var` `>>>` [blue]`var` +
[green]`3` [green]`3`
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`. NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
@@ -739,10 +745,10 @@ The table below shows all supported operators by decreasing priorities.
== Functions == Functions
Functions in _Expr_ are very similar to functions available in many programming languages. Actually, _Expr_ supports two types of function, _expr-functions_ and _go-functions_. Functions in _Expr_ are very similar to functions available in many programming languages. Currently, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures. * _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to _import_ the module in which they are defined. * _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to activate the builtin module or to load the plugin module in which they are defined.
=== _Expr_ function definition === _Expr_ function definition
@@ -750,10 +756,10 @@ A function is identified and referenced by its name. It can have zero or more pa
.Expr's function definition syntax .Expr's function definition syntax
==== ====
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**" *_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
_param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] _param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
_required-param-list_ = _identifier_ { "**,**" _identifier_ } _required-param-list_ = _identifier_ { "**,**" _identifier_ } +
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } _optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } +
_optional-param_ = _identifier_ "**=**" _any-expr_ _optional-param_ = _identifier_ "**=**" _any-expr_
==== ====
+49 -45
View File
@@ -657,7 +657,7 @@ pre.rouge .ss {
<p>Function contexts are created by cloning the calling context. More details on this topic are given later in this document.</p> <p>Function contexts are created by cloning the calling context. More details on this topic are given later in this document.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The createt context can be called <em>function context</em>.</p> <p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called <em>function context</em>.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>Imported functions are registerd in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p> <p>Imported functions are registerd in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p>
@@ -1109,7 +1109,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
</tbody> </tbody>
</table> </table>
<div class="paragraph"> <div class="paragraph">
<p>The items of strings can be accessed using the square <code>[]</code> operator.</p> <p>The charanters in a string can be accessed using the square <code>[]</code> operator.</p>
</div> </div>
<div class="exampleblock"> <div class="exampleblock">
<div class="title">Example 4. Item access syntax</div> <div class="title">Example 4. Item access syntax</div>
@@ -1137,11 +1137,11 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<code class="green">"b"</code></p> <code class="green">"b"</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">s.[-1]</code> <em class="gray">// char at position -1, the rightmost one</em><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">s[-1]</code> <em class="gray">// char at position -1, the rightmost one</em><br>
<code class="green">"d"</code></p> <code class="green">"d"</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">\#s</code> <em class="gray">// number of chars</em><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">#s</code> <em class="gray">// number of chars</em><br>
<code class="gren">4</code></p> <code class="gren">4</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
@@ -1208,14 +1208,14 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than the right one</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than the right one</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt; 2</code> &#8594; <em>true</em><br> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt; 2</code> &#8594; <em>true</em><br>
<code class="blue">"a" &lt; "b"</code> &#8594; <em>false</em></p></td> <code class="blue">"a" &gt; "b"</code> &#8594; <em>false</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;=</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;=</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater or Equal</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater or Equal</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than or equal to the right one</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than or equal to the right one</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt;= 2</code> &#8594; <em>true</em><br> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt;= 2</code> &#8594; <em>true</em><br>
<code class="blue">"b" &lt;= "b"</code> &#8594; <em>true</em></p></td> <code class="blue">"b" &gt;= "b"</code> &#8594; <em>true</em></p></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@@ -1256,7 +1256,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">OR</code> / <code class="blue">||</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">OR</code> / <code class="blue">||</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if at least one of the left and right values integers true</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">True if at least one of the left and right values integers is true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false or true</code> &#8594; <em>true</em><br> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false or true</code> &#8594; <em>true</em><br>
<code class="blue">"a" == "b" OR (2 == 1)</code> &#8594; <em>false</em></p></td> <code class="blue">"a" == "b" OR (2 == 1)</code> &#8594; <em>false</em></p></td>
</tr> </tr>
@@ -1314,7 +1314,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<div class="paragraph"> <div class="paragraph">
<p><strong><em>list</em></strong> = <em>empty-list</em> | <em>non-empty-list</em><br> <p><strong><em>list</em></strong> = <em>empty-list</em> | <em>non-empty-list</em><br>
<em>empty-list</em> = "<strong>[]</strong>"<br> <em>empty-list</em> = "<strong>[]</strong>"<br>
<em>non-empty-list</em> = "<strong>[</strong>" <em>any-value</em> {"<strong>,</strong>" _any-value} "<strong>]</strong>"<br></p> <em>non-empty-list</em> = "<strong>[</strong>" <em>any-value</em> {"<strong>,</strong>" <em>any-value</em>} "<strong>]</strong>"<br></p>
</div> </div>
</div> </div>
</div> </div>
@@ -1416,27 +1416,27 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Items of list</div> <div class="title">Items of list</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">[1,2,3].1</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">[1,2,3][1]</code><br>
<code class="green">2</code></p> <code class="green">2</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=[1,2,3]; list.1</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">list=[1,2,3]; list[1]</code><br>
<code class="green">2</code></p> <code class="green">2</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">["one","two","three"].1</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">["one","two","three"][1]</code><br>
<code class="green">two</code></p> <code class="green">two</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=["one","two","three"]; list.(2-1)</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">list=["one","two","three"]; list[2-1]</code><br>
<code class="green">two</code></p> <code class="green">two</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list.(-1)</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">list[-1]</code><br>
<code class="green">three</code></p> <code class="green">three</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list.(10)</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">list[10]</code><br>
<code class="red">Eval Error: [1:9] index 10 out of bounds</code></p> <code class="red">Eval Error: [1:9] index 10 out of bounds</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
@@ -1466,7 +1466,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<div class="paragraph"> <div class="paragraph">
<p><strong><em>dict</em></strong> = <em>empty-dict</em> | <em>non-empty-dict</em><br> <p><strong><em>dict</em></strong> = <em>empty-dict</em> | <em>non-empty-dict</em><br>
<em>empty-dict</em> = "<strong>{}</strong>"<br> <em>empty-dict</em> = "<strong>{}</strong>"<br>
<em>non-empty-dict</em> = "<strong>{</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em> {"<strong>,</strong>" <em>key-scalar</em> "<strong>:</strong>" _any-value} "<strong>}</strong>"<br></p> <em>non-empty-dict</em> = "<strong>{</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em> {"<strong>,</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em>} "<strong>}</strong>"<br></p>
</div> </div>
</div> </div>
</div> </div>
@@ -1575,7 +1575,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">a_b=1+2</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">a_b=1+2</code><br>
<code class="green">1+2</code></p> <code class="green">3</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">a_b</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">a_b</code><br>
@@ -1590,8 +1590,8 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
<code class="green">2</code></p> <code class="green">2</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue"><em>a=2</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">_a=2</code><br>
<code class="red">Parse Error: [1:2] unexpected token "</em>"</code></p> <code class="red">Parse Error: [1:2] unexpected token "_"</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">1=2</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">1=2</code><br>
@@ -1608,7 +1608,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
<p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.</p> <p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.</p>
</div> </div>
<div class="exampleblock"> <div class="exampleblock">
<div class="title">Example 12. Mult-expression syntax</div> <div class="title">Example 12. Multi-expression syntax</div>
<div class="content"> <div class="content">
<div class="paragraph"> <div class="paragraph">
<p><strong><em>multi-expression</em></strong> = <em>expression</em> {"<strong>;</strong>" <em>expression</em> }</p> <p><strong><em>multi-expression</em></strong> = <em>expression</em> {"<strong>;</strong>" <em>expression</em> }</p>
@@ -1616,7 +1616,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
</div> </div>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>An expression that contains <code class="blue">;</code> is called a <em>multi-expression</em> and each component expressione is called a <em>sub-expression</em>.</p> <p>An expression that contains <code class="blue">;</code> is called a <em>multi-expression</em> and each component expression is called a <em>sub-expression</em>.</p>
</div> </div>
<div class="admonitionblock important"> <div class="admonitionblock important">
<table> <table>
@@ -1648,7 +1648,7 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<code class="green">6</code></p> <code class="green">6</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>The value of each sub-expression is stored in the automatica variable <em>last</em>.</p> <p>The value of each sub-expression is stored in the automatic variable <em>last</em>.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Example</div> <div class="title">Example</div>
@@ -1663,9 +1663,11 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Examples</div> <div class="title">Examples</div>
<p><code class="blue">5 but 2</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">5 but 2</code><br>
<code class="green">2</code><br> <code class="green">2</code></p>
<code class="blue">x=2*3 but x-1</code><br> </div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">x=2*3 but x-1</code><br>
<code class="green">5</code>.</p> <code class="green">5</code>.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
@@ -1678,21 +1680,27 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<p>The assignment operator <code class="blue">=</code> is used to define variables or to change their value in the evaluation context (see <em>ExprContext</em>).</p> <p>The assignment operator <code class="blue">=</code> is used to define variables or to change their value in the evaluation context (see <em>ExprContext</em>).</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p> <p>The value on the left side of <code class="blue">=</code> must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Example</div> <div class="title">Examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">a=15+1</code> <p><code>&gt;&gt;&gt;</code> <code class="blue">a=15+1</code><br>
<code class="green">16</code></p> <code class="green">16</code></p>
</div> </div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">L=[1,2,3]; L[1]=5; L</code><br>
<code class="green">[1, 5, 3]</code></p>
</div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_selector_operator"><a class="anchor" href="#_selector_operator"></a><a class="link" href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></h3> <h3 id="_selector_operator"><a class="anchor" href="#_selector_operator"></a><a class="link" href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></h3>
<div class="paragraph"> <div class="paragraph">
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p> <p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
</div> </div>
<div class="exampleblock">
<div class="title">Example 13. Selector literal Syntax</div>
<div class="content">
<div class="paragraph"> <div class="paragraph">
<div class="title">Selector literal Syntax</div>
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br> <p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br> <em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br> <em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
@@ -1701,6 +1709,8 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<em>multi-expression</em> = <em>expression</em> { "<strong>;</strong>" <em>expression</em> }<br> <em>multi-expression</em> = <em>expression</em> { "<strong>;</strong>" <em>expression</em> }<br>
<em>default-multi-expression</em> = <em>multi-expression</em></p> <em>default-multi-expression</em> = <em>multi-expression</em></p>
</div> </div>
</div>
</div>
<div class="paragraph"> <div class="paragraph">
<p>In other words, the selector operator evaluates the <em>select-expression</em> on the left-hand side of the <code class="blue">?</code> symbol; it then compares the result obtained with the values listed in the <em>match-list</em>'s, from left to right. If the comparision finds a match with a value in a <em>match-list</em>, the associated <em>case-multi-expression</em> is evaluted, and its result will be the final result of the selection operation.</p> <p>In other words, the selector operator evaluates the <em>select-expression</em> on the left-hand side of the <code class="blue">?</code> symbol; it then compares the result obtained with the values listed in the <em>match-list</em>'s, from left to right. If the comparision finds a match with a value in a <em>match-list</em>, the associated <em>case-multi-expression</em> is evaluted, and its result will be the final result of the selection operation.</p>
</div> </div>
@@ -1765,8 +1775,8 @@ If the left variable is defined, the right expression is not evaluated at all.
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Examples</div> <div class="title">Examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">var ?? (1+2)&#8217;<br> <p><code>&gt;&gt;&gt;</code> <code class="blue">var ?? (1+2)</code><br>
[green]`3</code></p> <code class="green">3</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">var</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">var</code><br>
@@ -1777,7 +1787,7 @@ If the left variable is defined, the right expression is not evaluated at all.
<code class="green">3</code></p> <code class="green">3</code></p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">var</code> <p><code>&gt;&gt;&gt;</code> <code class="blue">var</code><br>
<code class="green">3</code></p> <code class="green">3</code></p>
</div> </div>
<div class="admonitionblock note"> <div class="admonitionblock note">
@@ -2106,7 +2116,7 @@ These operators have a high priority, in particular higher than the operator <co
<h2 id="_functions"><a class="anchor" href="#_functions"></a><a class="link" href="#_functions">6. Functions</a></h2> <h2 id="_functions"><a class="anchor" href="#_functions"></a><a class="link" href="#_functions">6. Functions</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p>Functions in <em>Expr</em> are very similar to functions available in many programming languages. Actually, <em>Expr</em> supports two types of function, <em>expr-functions</em> and <em>go-functions</em>.</p> <p>Functions in <em>Expr</em> are very similar to functions available in many programming languages. Currently, <em>Expr</em> supports two types of function, <em>expr-functions</em> and <em>go-functions</em>.</p>
</div> </div>
<div class="ulist"> <div class="ulist">
<ul> <ul>
@@ -2114,7 +2124,7 @@ These operators have a high priority, in particular higher than the operator <co
<p><em>expr-functions</em> are defined using <em>Expr</em>'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.</p> <p><em>expr-functions</em> are defined using <em>Expr</em>'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.</p>
</li> </li>
<li> <li>
<p><em>go-functions</em> are regular Golang functions callable from <em>Expr</em> expressions. They are defined in Golang source files called <em>modules</em> and compiled within the <em>Expr</em> package. To make Golang functions available in <em>Expr</em> contextes, it is required to <em>import</em> the module in which they are defined.</p> <p><em>go-functions</em> are regular Golang functions callable from <em>Expr</em> expressions. They are defined in Golang source files called <em>modules</em> and compiled within the <em>Expr</em> package. To make Golang functions available in <em>Expr</em> contextes, it is required to activate the builtin module or to load the plugin module in which they are defined.</p>
</li> </li>
</ul> </ul>
</div> </div>
@@ -2123,20 +2133,14 @@ These operators have a high priority, in particular higher than the operator <co
<div class="paragraph"> <div class="paragraph">
<p>A function is identified and referenced by its name. It can have zero or more parameter. <em>Expr</em> functions also support optional parameters.</p> <p>A function is identified and referenced by its name. It can have zero or more parameter. <em>Expr</em> functions also support optional parameters.</p>
</div> </div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Expr&#8217;s function definition syntax</p>
</li>
</ol>
</div>
<div class="exampleblock"> <div class="exampleblock">
<div class="title">Example 14. Expr&#8217;s function definition syntax</div>
<div class="content"> <div class="content">
<div class="paragraph"> <div class="paragraph">
<p><strong><em>function-definition</em></strong> = <em>identifier</em> "<strong>=</strong>" "<strong>func(</strong>" [<em>param-list</em>] "<strong>)</strong>" "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>" <p><strong><em>function-definition</em></strong> = <em>identifier</em> "<strong>=</strong>" "<strong>func(</strong>" [<em>param-list</em>] "<strong>)</strong>" "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>"<br>
<em>param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ] <em>param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]<br>
<em>required-param-list</em> = <em>identifier</em> { "<strong>,</strong>" <em>identifier</em> } <em>required-param-list</em> = <em>identifier</em> { "<strong>,</strong>" <em>identifier</em> }<br>
<em>optional-param-list</em> = <em>optional-parm</em> { "<strong>,</strong>" <em>optional-param</em> } <em>optional-param-list</em> = <em>optional-parm</em> { "<strong>,</strong>" <em>optional-param</em> }<br>
<em>optional-param</em> = <em>identifier</em> "<strong>=</strong>" <em>any-expr</em></p> <em>optional-param</em> = <em>identifier</em> "<strong>=</strong>" <em>any-expr</em></p>
</div> </div>
</div> </div>
@@ -2200,7 +2204,7 @@ These operators have a high priority, in particular higher than the operator <co
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2024-06-21 09:06:12 +0200 Last updated 2024-09-12 06:56:30 +0200
</div> </div>
</div> </div>
</body> </body>
+2 -3
View File
@@ -7,7 +7,6 @@ package expr
// ----Expression Context // ----Expression Context
type ExprContext interface { type ExprContext interface {
Clone() ExprContext Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext) SetParent(ctx ExprContext)
GetParent() (ctx ExprContext) GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
@@ -24,7 +23,7 @@ type ExprContext interface {
DeleteFunc(funcName string) DeleteFunc(funcName string)
GetFuncInfo(name string) (item ExprFunc, exists bool) 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) 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 // ---- Functor interface
type Functor interface { type Functor interface {
Typer 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) SetFunc(info ExprFunc)
GetFunc() ExprFunc GetFunc() ExprFunc
GetParams() []ExprFuncParam GetParams() []ExprFuncParam
@@ -32,7 +32,8 @@ type ExprFunc interface {
MaxArgs() int MaxArgs() int
Functor() Functor Functor() Functor
Params() []ExprFuncParam Params() []ExprFuncParam
ParamSpec(paramName string) ExprFuncParam
ReturnType() string 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) AllocContext(parentCtx ExprContext) (ctx ExprContext)
} }
+151 -21
View File
@@ -6,11 +6,12 @@ package expr
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
) )
// ---- Function template // ---- 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 // ---- Common functor definition
type baseFunctor struct { type baseFunctor struct {
@@ -137,10 +138,6 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
return info, nil 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 { func (info *funcInfo) Params() []ExprFuncParam {
return info.formalParams return info.formalParams
} }
@@ -216,37 +213,133 @@ func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
return return
} }
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) { func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
passedCount := len(*varActualParams) for _, spec := range info.formalParams {
if info.MinArgs() > passedCount { if spec.Name() == paramName {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount) return spec
}
}
return nil
} }
for i := passedCount; i < len(info.formalParams); i++ { func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
p := info.formalParams[i] var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
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
}
}
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 passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() { if !p.IsDefault() {
break break
} }
*varActualParams = append(*varActualParams, p.DefaultValue()) if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
} }
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
} }
if err == nil { if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
ctx = info.AllocContext(parentCtx) err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
} }
return return
} }
// ----- Call a function --- // ----- Call a function ---
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) { func getAssignVarName(t *term) (name string, ok bool) {
if info, exists, _ := GetFuncInfo(parentCtx, name); exists { if ok = t.symbol() == SymEqual; ok {
var ctx ExprContext name = t.children[0].source()
if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil { }
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() functor := info.Functor()
result, err = functor.Invoke(ctx, name, actualParams) 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) exportObjectsToParent(ctx)
} }
} else { } else {
@@ -254,3 +347,40 @@ func CallFunction(parentCtx ExprContext, name string, actualParams []any) (resul
} }
return 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 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 len(name) > 0 {
if item, exists = GetLocalFuncInfo(ctx, name); exists { if item, exists = GetLocalFuncInfo(ctx, name); exists {
ownerCtx = ctx ownerCtx = ctx
+3 -1
View File
@@ -33,8 +33,10 @@ type Iterator interface {
type ExtIterator interface { type ExtIterator interface {
Iterator Iterator
Reset() error
Clean() error
HasOperation(name string) bool 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 { func errNoOperation(name string) error {
+10 -4
View File
@@ -94,12 +94,14 @@ func (it *ListIterator) HasOperation(name string) bool {
return yes 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 { switch name {
case NextName: case NextName:
v, err = it.Next() v, err = it.Next()
case ResetName: case ResetName:
v, err = it.Reset() err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName: case IndexName:
v = int64(it.Index()) v = int64(it.Index())
case CurrentName: case CurrentName:
@@ -147,8 +149,12 @@ func (it *ListIterator) Count() int {
return it.count return it.count
} }
func (it *ListIterator) Reset() (bool, error) { func (it *ListIterator) Reset() (error) {
it.index = it.start - it.step it.index = it.start - it.step
it.count = 0 it.count = 0
return true, nil 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 // -------- 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) { func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := opTerm.tk.Value.(string) name, _ := opTerm.tk.Value.(string)
params := make([]any, len(opTerm.children), len(opTerm.children)+5) v, err = CallFunctionByTerm(ctx, name, opTerm)
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 return
} }
+6 -18
View File
@@ -11,22 +11,6 @@ import (
// -------- iterator term // -------- 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 { func newIteratorTerm(tk *Token, args []*term) *term {
tk.Sym = SymIterator tk.Sym = SymIterator
return &term{ return &term{
@@ -108,6 +92,7 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
if ds != nil { if ds != nil {
var dc *dataCursor var dc *dataCursor
dcCtx := ctx.Clone()
if initFunc, exists := ds[InitName]; exists && initFunc != nil { if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any var args []any
var resource any var resource any
@@ -119,13 +104,16 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
args = []any{} args = []any{}
} }
actualParams := bindActualParams(initFunc, args)
initCtx := ctx.Clone() initCtx := ctx.Clone()
if resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil { if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
return return
} }
dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx) exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource) dc = NewDataCursor(dcCtx, ds, resource)
} else {
dc = NewDataCursor(dcCtx, ds, nil)
} }
v = dc v = dc
+1 -1
View File
@@ -25,7 +25,7 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
var exists bool var exists bool
name := opTerm.source() name := opTerm.source()
if v, exists = GetVar(ctx, name); !exists { if v, exists = GetVar(ctx, name); !exists {
if info, exists, _ := GetFuncInfo(ctx, name); exists { if info, exists := GetFuncInfo(ctx, name); exists {
v = info.Functor() v = info.Functor()
} else { } else {
err = fmt.Errorf("undefined variable or function %q", name) 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 */ { if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
opName := indexTerm.source() opName := indexTerm.source()
if unboxedValue.HasOperation(opName) { if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, []any{}) v, err = unboxedValue.CallOperation(opName, map[string]any{})
} else { } else {
err = indexTerm.Errorf("this iterator do not support the %q command", opName) err = indexTerm.Errorf("this iterator do not support the %q command", opName)
v = false v = false
+4 -3
View File
@@ -149,10 +149,11 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
ctx.funcStore[info.Name()], _ = info.(*funcInfo) 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 var info *funcInfo
if info, err = newFuncInfo(name, functor, returnType, params); err == nil { if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
ctx.funcStore[name] = info ctx.funcStore[name] = info
exprFunc = info
} }
return return
} }
@@ -179,10 +180,10 @@ func (ctx *SimpleStore) DeleteFunc(funcName string) {
delete(ctx.funcStore, funcName) 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 { if info, exists := GetLocalFuncInfo(ctx, name); exists {
functor := info.Functor() functor := info.Functor()
result, err = functor.Invoke(ctx, name, args) result, err = functor.InvokeNamed(ctx, name, args)
} else { } else {
err = fmt.Errorf("unknown function %s()", name) err = fmt.Errorf("unknown function %s()", name)
} }
+3 -2
View File
@@ -21,7 +21,7 @@ func TestFuncBase(t *testing.T) {
/* 7 */ {`int(3.9)`, int64(3), nil}, /* 7 */ {`int(3.9)`, int64(3), nil},
/* 8 */ {`int("432")`, int64(432), nil}, /* 8 */ {`int("432")`, int64(432), nil},
/* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`}, /* 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`}, /* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
/* 12 */ {`isInt(2+1)`, true, nil}, /* 12 */ {`isInt(2+1)`, true, nil},
/* 13 */ {`isInt(3.1)`, false, nil}, /* 13 */ {`isInt(3.1)`, false, nil},
@@ -43,7 +43,7 @@ func TestFuncBase(t *testing.T) {
/* 29 */ {`dec(true)`, float64(1), nil}, /* 29 */ {`dec(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`}, /* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`}, /* 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}, /* 33 */ {`isBool(false)`, true, nil},
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil}, /* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil}, /* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
@@ -61,5 +61,6 @@ func TestFuncBase(t *testing.T) {
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -30,7 +30,7 @@ func TestFmt(t *testing.T) {
text := "ciao mondo" text := "ciao mondo"
inputs := []inputType{ 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", ".") // t.Setenv("EXPR_PATH", ".")
+4 -2
View File
@@ -17,12 +17,14 @@ func TestFuncRun(t *testing.T) {
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"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}, /* 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]`}, /* 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]`}, /* 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", ".") //t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3) //runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+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}, /* 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}, /* 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}, /* 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", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1) //runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -30,6 +30,6 @@ func TestFuncOs(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1) //runTestSuiteSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+5 -3
View File
@@ -42,6 +42,8 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
failed := 0 failed := 0
for i, input := range inputs { for i, input := range inputs {
// fmt.Printf("%3d: %s\n", i+1, input.source)
good := doTest(t, ctx, section, &input, i+1) good := doTest(t, ctx, section, &input, i+1)
if good { if good {
succeeded++ succeeded++
@@ -91,7 +93,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
eq := reflect.DeepEqual(gotResult, input.wantResult) eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*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 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) { func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
if wantErr == nil { 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 { } else {
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr) t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
} }
} }
+1 -1
View File
@@ -43,6 +43,6 @@ func TestExpr(t *testing.T) {
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 9) // runTestSuiteSpec(t, section, inputs, 17)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+8 -4
View File
@@ -24,12 +24,17 @@ 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}, /* 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}, /* 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}, /* 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}, /* 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`}, /* 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}, /* 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}, /* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, 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 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, 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 ")"`)}, // /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
@@ -37,11 +42,11 @@ func TestFuncs(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 20) //runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs) 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 return
} }
@@ -54,7 +59,6 @@ func TestFunctionToStringSimple(t *testing.T) {
} }
} }
func TestFunctionGetFunc(t *testing.T) { func TestFunctionGetFunc(t *testing.T) {
source := NewGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := ExprFunc(nil) want := ExprFunc(nil)
+40 -39
View File
@@ -5,50 +5,51 @@
package expr package expr
import ( import (
"fmt"
"testing" "testing"
) )
func subtract(ctx ExprContext, name string, args []any) (result any, err error) { // TODO The new function param model does not allow this kind of test
if len(args) != 2 { // ------------------------------------------------------------------
err = fmt.Errorf("%s(): requires exactly two arguments", name) // func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return // if len(args) != 2 {
} // err = fmt.Errorf("%s(): requires exactly two arguments", name)
x, xok := args[0].(int64) // return
y, yok := args[1].(int64) // }
if xok && yok { // x, xok := args[0].(int64)
result = x - y // y, yok := args[1].(int64)
} else { // if xok && yok {
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y) // result = x - y
} // } else {
return // 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)` // source := `a + b * subtract(4,2)`
args := []Arg{ // args := []Arg{
{"a", uint8(1)}, // {"a", uint8(1)},
{"b", int8(2)}, // {"b", int8(2)},
{"subtract", FuncTemplate(subtract)}, // {"subtract", FuncTemplate2(subtract)},
// force coverage // // force coverage
{"a16", uint16(1)}, // {"a16", uint16(1)},
{"b16", int16(2)}, // {"b16", int16(2)},
{"a32", uint32(1)}, // {"a32", uint32(1)},
{"b32", int32(2)}, // {"b32", int32(2)},
{"a64", uint64(1)}, // {"a64", uint64(1)},
{"b64", int64(2)}, // {"b64", int64(2)},
{"f32", float32(1.0)}, // {"f32", float32(1.0)},
{"f64", float64(1.0)}, // {"f64", float64(1.0)},
} // }
wantResult := int64(5) // wantResult := int64(5)
gotResult, gotErr := EvalStringA(source, args...) // gotResult, gotErr := EvalStringA(source, args...)
if value, ok := gotResult.(int64); ok && value != wantResult { // if value, ok := gotResult.(int64); ok && value != wantResult {
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult) // t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
t.Errorf("Error: %v", gotErr) // t.Errorf("Error: %v", gotErr)
} // }
} // }
func TestEvalString(t *testing.T) { func TestEvalString(t *testing.T) {
@@ -68,7 +69,7 @@ func TestEvalString(t *testing.T) {
// force coverage // force coverage
ctx.GetFuncInfo("dummy") ctx.GetFuncInfo("dummy")
ctx.Call("dummy", []any{}) ctx.Call("dummy", map[string]any{})
source := `a + b * f` source := `a + b * f`
+4 -2
View File
@@ -17,7 +17,7 @@ func TestIteratorParser(t *testing.T) {
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), 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}, /* 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}, /* 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}, /* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
/* 11 */ {`it=$(1,2,3); it++; it.reset; 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}, /* 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}, /* 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`}, /* 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}, /* 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, 3) //runTestSuiteSpec(t, section, inputs, 18)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }