Compare commits

...

78 Commits

Author SHA1 Message Date
camoroso dceb31f542 CallFunction() has been replaced by three new functions:
CallFunctionByTerm(), CallFunctionByArgs() and CallFunctionByParams()
2024-08-02 06:39:33 +02:00
camoroso 075b0b5691 RegisterFunc() also returns the funcInfo object 2024-08-01 00:09:49 +02:00
camoroso 3c51b8d2ee Changed some function names and param types 2024-07-31 09:11:57 +02:00
camoroso a46753f453 Function buildActualParams moved from data-cursor.go ro function.go 2024-07-31 09:08:58 +02:00
camoroso 9070b5c9cc The function parameter model has been modified to support the passing of named parameters 2024-07-28 18:49:08 +02:00
camoroso ab06702e5e operator-post-inc.go: new post int decrement operator '--' 2024-07-24 06:39:35 +02:00
camoroso ffe1fa3aac op-assign expansion now end at ']' and '}' too 2024-07-24 06:37:57 +02:00
camoroso 1a1a475dd8 Added support to op-assign operators like '+='.
This feature is implemented by expansion, not by dedicated operators, e.g. a*=2+x is exapanded as a=a*(2+x)
2024-07-23 15:35:57 +02:00
camoroso 463e3634ba scanner.go: New function UnreadToken().
Currently it only supports one staging level.
2024-07-23 15:32:25 +02:00
camoroso 5f8ca47ef0 term.go: New function Clone() 2024-07-23 15:27:50 +02:00
camoroso 837b887490 token.go: New functions Clone() and IsOneOfA() 2024-07-23 15:27:36 +02:00
camoroso c76e1d3c8e symbol.go: New symbol '*=' 2024-07-23 15:24:54 +02:00
camoroso 315f5b22d3 data-cursor.go: the inizialization of the current item is done in the Next() method.
This allows the application of the filter and map operator to the first item too.
2024-07-23 05:46:37 +02:00
camoroso dfae593e86 operand-iterator.go: removed commented code 2024-07-21 16:35:13 +02:00
camoroso d572f3a129 Iterator: new function Count() 2024-07-21 16:34:52 +02:00
camoroso c461fd138e Iterator defined by data-source now only requires one method: next() 2024-07-21 05:45:22 +02:00
camoroso 6ecbe2feb1 Fixed type (expcted -> expected) 2024-07-21 05:43:55 +02:00
camoroso 80d3c6ec7d parser.go: Fixed an old bug that did not allow the parser to skip comment tokens 2024-07-21 05:37:34 +02:00
camoroso e09806c716 %q replaced by %s in some error messages 2024-07-21 05:33:06 +02:00
camoroso 1a772597cb removed/commented unused code 2024-07-19 17:03:03 +02:00
camoroso 33b3e1fc29 New function for searching and importing plugin 2024-07-19 15:37:00 +02:00
camoroso 4e3f5cfbc6 import-utils.go: Paths are now expanded with respect to env-vars and shell ~ 2024-07-19 15:33:15 +02:00
camoroso e35d4e3f70 utils.go: added function ExpandPath 2024-07-19 15:30:26 +02:00
camoroso b4529499d6 iterator.go: exported some const identifier 2024-07-18 07:27:02 +02:00
camoroso 7745dc24e2 dict-type.go: exported NewDataCursor 2024-07-18 07:25:51 +02:00
camoroso be25385d02 builtin-iterator: changed status variable from 'it_status' to 'status' 2024-07-15 06:59:13 +02:00
camoroso 79889cd8e1 New builtin module 'iterator' 2024-07-14 16:53:32 +02:00
camoroso 234759158c scanner: disabled prefix operator '()' 2024-07-13 18:10:04 +02:00
camoroso 00fda29606 operator-iter-value.go: prefix operator () used to get the current value of an iterator replaced by ^ 2024-07-13 17:17:16 +02:00
camoroso 2a2840bdf2 ListIterator now implements next and current command (e.g it.next) 2024-07-13 17:15:53 +02:00
camoroso 06373f5126 Impemented Typer interface 2024-07-13 17:14:25 +02:00
camoroso e69dad5fb5 Rename priority label priCoalesce as priDefault 2024-07-13 17:13:16 +02:00
camoroso 7459057df9 Expr embeds Typer and ast implements it 2024-07-13 17:11:39 +02:00
camoroso 176969c956 New iterator tests 2024-07-13 16:19:04 +02:00
camoroso cde2efacf1 Test on iterator filter and map 2024-07-13 15:30:04 +02:00
camoroso d7a7b3218c Merge branch 'main' into enhance_iterators 2024-07-13 09:08:16 +02:00
camoroso be3bb12f28 operator-unset.go: fixed function removal 2024-07-13 09:07:33 +02:00
camoroso 032916d4fa New operator unset to delete variables and functions from current context 2024-07-13 09:01:59 +02:00
camoroso f3cc0cc7ad operator-include.go: Fixed inclusion of a list of files.
Now returns the value of the last evaluated expression.
2024-07-13 09:00:53 +02:00
camoroso 905337f963 ExprContext: new functions VarCount(), DeleteVar(), FuncCount(), DeleteFunc() 2024-07-13 08:59:15 +02:00
camoroso 9a95a837f6 data-cursor.go: add item mapping support 2024-07-13 06:44:00 +02:00
camoroso 8547248ea2 data-cursor.go: fixed filter 2024-07-13 00:12:08 +02:00
camoroso a02f998fc6 t_iterator_test.go: Fixed test numbering and add a commentend test that is not yet fulfilled 2024-07-11 07:23:35 +02:00
camoroso e904003e6e minor changes 2024-07-11 06:53:14 +02:00
camoroso 990e04f957 operator-assign.go -- Fix: Assigning a functor to a collection's item didn't work 2024-07-11 06:49:02 +02:00
camoroso d8aed9dd7a t_iterator_test.go: added a test to verify the reset command of the list iterator 2024-07-11 05:54:22 +02:00
camoroso a73d24b171 iter-list.go -> list-iterator.go
Fix: the reset command set the initial item to the second one in the list
2024-07-11 05:52:47 +02:00
camoroso 3ebba83bce term.go: replaced self receiver 2024-07-09 07:50:50 +02:00
camoroso 6b3bfa2a11 self param replaced as opTerm 2024-07-09 07:50:06 +02:00
camoroso 867806155e scanner.go: replaced self receiver 2024-07-08 07:30:58 +02:00
camoroso a711333a2e simple-store.go: commented out an unused function 2024-07-08 07:30:26 +02:00
camoroso af3e946bd4 operator-sum.go: replaced self receiver 2024-07-08 07:29:42 +02:00
camoroso 22a36fa630 context.go renamed as expr-context.go 2024-07-07 16:20:29 +02:00
camoroso 6d9a379c92 token.go: replaced self receiver 2024-07-07 16:19:58 +02:00
camoroso dd6404c786 t_scanner_test.go: replaced t.Log(fmtStringf()) with t.Logf() 2024-07-07 16:18:39 +02:00
camoroso 34874ef663 plugins.go: replaced stringsIndex()<0 with !strings.Contains() 2024-07-07 16:17:48 +02:00
camoroso 9bc8e8ca05 operator-sum.go: replaced for-loop with append() 2024-07-07 16:15:56 +02:00
camoroso 7f367cfc49 parser.go: renamed self receiver 2024-07-07 16:14:52 +02:00
camoroso fe999acf2c operator-index.go: removed unused parameter from the function verifyKey() 2024-07-07 16:14:04 +02:00
camoroso 2ed1a1842b operand-iterator.go: commented out an unused function and replaced self receiver 2024-07-07 16:10:43 +02:00
camoroso bb9493d0cc list-type.go: commented out an unused fuction 2024-07-07 16:08:45 +02:00
camoroso f279bf163e import-utils.go: commented out unused fuctions 2024-07-07 15:59:23 +02:00
camoroso 6834d9f47b function.go: removed useless param != nil check 2024-07-07 15:58:29 +02:00
camoroso 8051faa2bf fraction-type.go: use of strings.TrimSuffix() in place of check suffix and slice 2024-07-07 15:57:17 +02:00
camoroso f30e687a79 changed the file name comment 2024-07-07 15:55:51 +02:00
camoroso 2b6e46576b byte-slider.go: renamed function receiver from self to slider 2024-07-07 15:54:26 +02:00
camoroso dc06c03112 builtin-string.go: removed useless err == nil check 2024-07-07 15:53:29 +02:00
camoroso e8f5d3e445 builtin-base.go: unused function iteratorFunc() commented out 2024-07-07 15:52:16 +02:00
camoroso 76ce0945f7 context.go splitted in two files: expr-context.go and expr-function-go.
Expr interface moved from ast.go to the new file expr.go
2024-07-07 15:51:29 +02:00
camoroso 340b99bad7 list-type.go: use of copy() for copying lists 2024-07-07 07:34:58 +02:00
camoroso 93dac956fb t_parser_test.go: more tests on ??, ?= and ?! operators 2024-07-06 17:01:23 +02:00
camoroso 307027d23d t_parser_test.go: changed all error values to string values 2024-07-06 16:47:00 +02:00
camoroso 896844e772 the common test framework now supports error, string and nil as value of the wantErr field 2024-07-06 16:43:13 +02:00
camoroso 9fc20077a1 operator-include.go: the include operator did not count the number of files included when its argument was a single string 2024-07-06 16:08:58 +02:00
camoroso d2a4adebdd dataCursor implements Typer interface 2024-07-06 16:05:54 +02:00
camoroso fd8e32e12b new operator "?!" (alternate value) 2024-07-06 05:54:53 +02:00
camoroso 1e62a51c15 t_fractions_test.go: added a test on the string() function 2024-07-06 04:56:00 +02:00
camoroso 0170baa0f5 iter-list.go: implemented the Typer interface 2024-07-06 04:54:42 +02:00
93 changed files with 2135 additions and 1284 deletions
+37 -38
View File
@@ -8,11 +8,6 @@ import (
"strings" "strings"
) )
type Expr interface {
Eval(ctx ExprContext) (result any, err error)
String() string
}
//-------- ast //-------- ast
type ast struct { type ast struct {
@@ -24,65 +19,69 @@ func NewAst() *ast {
return &ast{} return &ast{}
} }
func (self *ast) ToForest() { func (expr *ast) TypeName() string {
if self.root != nil { return "Expression"
if self.forest == nil { }
self.forest = make([]*term, 0)
func (expr *ast) ToForest() {
if expr.root != nil {
if expr.forest == nil {
expr.forest = make([]*term, 0)
} }
self.forest = append(self.forest, self.root) expr.forest = append(expr.forest, expr.root)
self.root = nil expr.root = nil
} }
} }
func (self *ast) String() string { func (expr *ast) String() string {
var sb strings.Builder var sb strings.Builder
if self.root == nil { if expr.root == nil {
sb.WriteString("(nil)") sb.WriteString("(nil)")
} else { } else {
self.root.toString(&sb) expr.root.toString(&sb)
} }
return sb.String() return sb.String()
} }
func (self *ast) addTokens(tokens ...*Token) (err error) { func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens { for _, tk := range tokens {
if err = self.addToken(tk); err != nil { if _, err = expr.addToken(tk); err != nil {
break break
} }
} }
return return
} }
func (self *ast) addToken(tk *Token) (err error) { // func (expr *ast) addToken(tk *Token) (err error) {
_, err = self.addToken2(tk) // _, err = expr.addToken2(tk)
return // return
} // }
func (self *ast) addToken2(tk *Token) (t *term, err error) { func (expr *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil { if t = newTerm(tk); t != nil {
err = self.addTerm(t) err = expr.addTerm(t)
} else { } else {
err = tk.Errorf("unexpected token %q", tk.String()) err = tk.Errorf("unexpected token %q", tk.String())
} }
return return
} }
func (self *ast) addTerm(node *term) (err error) { func (expr *ast) addTerm(node *term) (err error) {
if self.root == nil { if expr.root == nil {
self.root = node expr.root = node
} else { } else {
self.root, err = self.insert(self.root, node) expr.root, err = expr.insert(expr.root, node)
} }
return return
} }
func (self *ast) insert(tree, node *term) (root *term, err error) { func (expr *ast) insert(tree, node *term) (root *term, err error) {
if tree.getPriority() < node.getPriority() { if tree.getPriority() < node.getPriority() {
root = tree root = tree
if tree.isComplete() { if tree.isComplete() {
var subRoot *term var subRoot *term
last := tree.removeLastChild() last := tree.removeLastChild()
if subRoot, err = self.insert(last, node); err == nil { if subRoot, err = expr.insert(last, node); err == nil {
subRoot.setParent(tree) subRoot.setParent(tree)
} }
} else { } else {
@@ -97,20 +96,20 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
return return
} }
func (self *ast) Finish() { func (expr *ast) Finish() {
if self.root == nil && self.forest != nil && len(self.forest) >= 1 { if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
self.root = self.forest[len(self.forest)-1] expr.root = expr.forest[len(expr.forest)-1]
self.forest = self.forest[0 : len(self.forest)-1] expr.forest = expr.forest[0 : len(expr.forest)-1]
} }
} }
func (self *ast) Eval(ctx ExprContext) (result any, err error) { func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
self.Finish() expr.Finish()
if self.root != nil { if expr.root != nil {
// initDefaultVars(ctx) // initDefaultVars(ctx)
if self.forest != nil { if expr.forest != nil {
for _, root := range self.forest { for _, root := range expr.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} else { } else {
@@ -120,7 +119,7 @@ func (self *ast) Eval(ctx ExprContext) (result any, err error) {
} }
} }
if err == nil { if err == nil {
if result, err = self.root.compute(ctx); err == nil { if result, err = expr.root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} }
} }
+22 -19
View File
@@ -12,35 +12,30 @@ 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
// defCtx = ctx.Clone()
// defCtx.SetParent(ctx)
// } else {
defCtx = ctx
// }
} }
return &exprFunctor{expr: e, params: params, defCtx: defCtx} return &exprFunctor{expr: e, params: params, defCtx: defCtx}
} }
func (functor *exprFunctor) TypeName() string {
return "ExprFunctor"
}
func (functor *exprFunctor) GetDefinitionContext() ExprContext { 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)
@@ -48,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)
} }
} }
result, err = functor.expr.Eval(ctx) if missing != nil {
err = ErrMissingParams(name, missing)
} else {
result, err = functor.expr.Eval(ctx)
}
return return
} }
+5 -1
View File
@@ -14,6 +14,10 @@ func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f} return &golangFunctor{f: f}
} }
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
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)
} }
+43 -39
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)
} }
@@ -180,9 +184,9 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
return return
} }
func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) { // func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return // return
} // }
func ImportBuiltinsFuncs(ctx ExprContext) { func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{ anyParams := []ExprFuncParam{
@@ -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
} }
+104
View File
@@ -0,0 +1,104 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-iterator.go
package expr
import (
"fmt"
"io"
)
const (
iterParamOperator = "operator"
iterParamVars = "vars"
iterVarStatus = "status"
)
func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Functor, err error) {
var ok bool
if it, ok = args[ParamIterator].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
return
}
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
return
}
var vars *DictType
if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], TypeName(args[iterParamVars]))
return
}
if vars != nil {
for key, value := range *vars {
var varName string
if varName, ok = key.(string); ok {
localCtx.UnsafeSetVar(varName, value)
}
}
}
return
}
func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var it Iterator
var ok bool
var op Functor
var v any
var usingDefaultOp = false
var params map[string]any
var item any
localCtx := ctx.Clone()
localCtx.UnsafeSetVar(iterVarStatus, nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil {
return
} else if op == nil {
op = NewGolangFunctor(printLnFunc)
usingDefaultOp = true
}
for item, err = it.Next(); err == nil; item, err = it.Next() {
if usingDefaultOp {
params = map[string]any{ParamItem: []any{item}}
} else {
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
if success, ok = ToBool(v); !success || !ok {
break
}
}
}
if err == io.EOF {
err = nil
}
if err == nil {
result, _ = localCtx.GetVar(iterVarStatus)
}
return
}
func ImportIterFuncs(ctx ExprContext) {
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamIterator),
NewFuncParamFlag(iterParamOperator, PfOptional),
NewFuncParamFlag(iterParamVars, PfOptional),
})
}
func init() {
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
}
+12 -10
View File
@@ -34,8 +34,8 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doAdd(ctx, name, subIter, count, level); err != nil { if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = extIter.CallOperation(CleanName, nil); err != nil {
return return
} }
} }
@@ -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
} }
@@ -107,8 +108,8 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
if v, err = doMul(ctx, name, subIter, count, level); err != nil { if v, err = doMul(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = extIter.CallOperation(CleanName, nil); err != nil {
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),
}) })
} }
+78 -56
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
@@ -25,52 +29,52 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return return
} }
} }
if err == nil || err == io.EOF { if err == io.EOF {
err = nil err = nil
result = sb.String() result = sb.String()
} }
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 = "" result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if len(args) == 2 { } else if it, ok := argv[0].(Iterator); ok {
if ls, ok := args[1].(*ListType); ok { result, err = doJoinStr(name, sep, it)
result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) } else if s, ok := argv[0].(string); ok {
} else if it, ok := args[1].(Iterator); ok { result = s
result, err = doJoinStr(name, sep, it) } else {
err = ErrInvalidParameterValue(name, ParamItem, v)
}
} else { } else {
err = ErrInvalidParameterValue(name, ParamParts, args[1]) result, err = doJoinStr(name, sep, NewArrayIterator(argv))
} }
} else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
} }
} 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 target, ok := targetSpec.(string); ok { if prefix, ok = args[ParamPrefix].(string); !ok {
if strings.HasPrefix(source, target) { return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
result = true }
if strings.HasPrefix(source, prefix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break break
} }
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
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 target, ok := targetSpec.(string); ok { if suffix, ok = args[ParamSuffix].(string); !ok {
if strings.HasSuffix(source, target) { return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
result = true }
if strings.HasPrefix(source, suffix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break break
} }
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
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),
}) })
} }
+9 -9
View File
@@ -18,17 +18,17 @@ func NewByteSlider(size int) *ByteSlider {
} }
} }
func (self *ByteSlider) PushEnd(b byte) { func (slider *ByteSlider) PushEnd(b byte) {
if self.length == cap(self.buf) { if slider.length == cap(slider.buf) {
self.length-- slider.length--
for i := 0; i < self.length; i++ { for i := 0; i < slider.length; i++ {
self.buf[i] = self.buf[i+1] slider.buf[i] = slider.buf[i+1]
} }
} }
self.buf[self.length] = b slider.buf[slider.length] = b
self.length++ slider.length++
} }
func (self *ByteSlider) Equal(target []byte) bool { func (slider *ByteSlider) Equal(target []byte) bool {
return target != nil && bytes.Equal(self.buf, target) return target != nil && bytes.Equal(slider.buf, target)
} }
+17 -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
} }
@@ -53,3 +58,13 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error { func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
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
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
}
+6 -1
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,9 +21,12 @@ const (
ParamEllipsis = "..." ParamEllipsis = "..."
ParamFilepath = "filepath" ParamFilepath = "filepath"
ParamDirpath = "dirpath" ParamDirpath = "dirpath"
ParamHandle = "handle"
ParamResource = "resource"
ParamIterator = "iterator"
) )
// to be moved in its own source file // to be moved in its own source file
const ( const (
ConstLastIndex = 0xFFFF_FFFF ConstLastIndex = 0xFFFF_FFFF
) )
+11 -11
View File
@@ -5,16 +5,16 @@
package expr package expr
const ( const (
TypeAny = "any" TypeAny = "any"
TypeBoolean = "boolean" TypeBoolean = "boolean"
TypeFloat = "float" TypeFloat = "float"
TypeFraction = "fraction" TypeFraction = "fraction"
TypeHandle = "handle" TypeFileHandle = "file-handle"
TypeInt = "integer" TypeInt = "integer"
TypeItem = "item" TypeItem = "item"
TypeNumber = "number" TypeNumber = "number"
TypePair = "pair" TypePair = "pair"
TypeString = "string" TypeString = "string"
TypeListOf = "list-of-" TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings" TypeListOfStrings = "list-of-strings"
) )
-55
View File
@@ -1,55 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// context.go
package expr
// ---- Functor interface
type Functor interface {
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
GetDefinitionContext() ExprContext
}
// ---- Function Param Info
type ExprFuncParam interface {
Name() string
Type() string
IsDefault() bool
IsOptional() bool
IsRepeat() bool
DefaultValue() any
}
// ---- Function Info
type ExprFunc interface {
Formatter
Name() string
MinArgs() int
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ReturnType() string
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
}
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
}
+126 -40
View File
@@ -5,30 +5,49 @@
package expr package expr
import ( import (
"errors"
"io" "io"
) )
type dataCursor struct { type dataCursor struct {
ds map[string]Functor ds map[string]Functor
ctx ExprContext ctx ExprContext
index int initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
resource any index int
nextFunc Functor count int
cleanFunc Functor current any
resetFunc Functor lastErr error
currentFunc Functor resource any
nextFunc Functor
cleanFunc Functor
resetFunc Functor
} }
func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
index: -1, initState: true,
ctx: ctx.Clone(), index: -1,
count: 0,
current: nil,
lastErr: nil,
resource: resource,
ctx: ctx.Clone(),
nextFunc: ds[NextName],
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
} }
//dc.Next()
return return
} }
func (dc *dataCursor) Context() ExprContext {
return dc.ctx
}
func (dc *dataCursor) TypeName() string {
return "DataCursor"
}
// func mapToString(m map[string]Functor) string { // func mapToString(m map[string]Functor) string {
// var sb strings.Builder // var sb strings.Builder
// sb.WriteByte('{') // sb.WriteByte('{')
@@ -58,7 +77,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 = name == IndexName
if !exists { if !exists {
f, ok := dc.ds[name] f, ok := dc.ds[name]
exists = ok && isFunctor(f) exists = ok && isFunctor(f)
@@ -66,8 +85,8 @@ 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 functor, ok := dc.ds[name]; ok && isFunctor(functor) { } else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc { if functor == dc.cleanFunc {
@@ -76,7 +95,7 @@ func (dc *dataCursor) CallOperation(name string, args []any) (value any, err err
value, err = dc.Reset() value, err = dc.Reset()
} else { } 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 {
@@ -89,15 +108,20 @@ func (dc *dataCursor) Reset() (success bool, err error) {
if dc.resetFunc != nil { if dc.resetFunc != nil {
if dc.resource != nil { if dc.resource != nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil { actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
dc.index = -1 _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
}
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
//dc.Next()
} else { } else {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
} else { } else {
err = errNoOperation(resetName) err = errNoOperation(ResetName)
} }
success = err == nil success = err == nil
return return
@@ -107,48 +131,110 @@ func (dc *dataCursor) Clean() (success bool, err error) {
if dc.cleanFunc != nil { if dc.cleanFunc != nil {
if dc.resource != 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)
// dc.resource = nil
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
} else { } else {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
} else { } else {
err = errors.New("no 'clean' function defined in the data-source") err = errNoOperation(CleanName)
} }
success = err == nil success = err == nil
return 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
ctx := cloneContext(dc.ctx) dc.init()
if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
if dc.current != nil {
item = dc.current
} else {
err = io.EOF err = io.EOF
} }
exportObjects(dc.ctx, ctx)
return return
} }
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
var v any
var ok bool
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(filter, []any{item, dc.index})
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil {
if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
}
return
}
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(mapper, []any{item, dc.index})
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
return
}
func (dc *dataCursor) init() {
if dc.initState {
dc.initState = false
dc.Next()
}
}
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 { if dc.resource != nil {
ctx := cloneContext(dc.ctx) filter := dc.ds[FilterName]
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) mapper := dc.ds[MapName]
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil { var item any
if item == nil { for item == nil && dc.lastErr == nil {
err = io.EOF ctx := cloneContext(dc.ctx)
} else { dc.index++
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()
} }
// fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
exportObjects(dc.ctx, ctx)
// fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
} else { } else {
err = errInvalidDataSource() dc.lastErr = errInvalidDataSource()
} }
return return
} }
func (dc *dataCursor) Index() int { func (dc *dataCursor) Index() int {
return dc.index return dc.index - 1
}
func (dc *dataCursor) Count() int {
return dc.count
} }
+15
View File
@@ -18,7 +18,22 @@ func MakeDict() (dict *DictType) {
return return
} }
func NewDict(dictAny map[any]any) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func newDict(dictAny map[any]*term) (dict *DictType) { func newDict(dictAny map[any]*term) (dict *DictType) {
// TODO Change wi a call to NewDict()
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
d = make(DictType, len(dictAny)) d = make(DictType, len(dictAny))
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr-context.go
package expr
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string)
VarCount() int
DeleteVar(varName string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
FuncCount() int
DeleteFunc(funcName string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args map[string]any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr-function.go
package expr
// ---- Functor interface
type Functor interface {
Typer
InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
GetDefinitionContext() ExprContext
}
// ---- Function Param Info
type ExprFuncParam interface {
Name() string
Type() string
IsDefault() bool
IsOptional() bool
IsRepeat() bool
DefaultValue() any
}
// ---- Function Info
type ExprFunc interface {
Formatter
Name() string
MinArgs() int
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ParamSpec(paramName string) ExprFuncParam
ReturnType() string
PrepareCall(name string, actualParams map[string]any) (err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
}
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr.go
package expr
// ----Expression interface
type Expr interface {
Typer
Eval(ctx ExprContext) (result any, err error)
String() string
}
+4 -4
View File
@@ -50,9 +50,10 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
} else if s[0] == '+' { } else if s[0] == '+' {
s = s[1:] s = s[1:]
} }
if strings.HasSuffix(s, "()") { // if strings.HasSuffix(s, "()") {
s = s[0 : len(s)-2] // s = s[0 : len(s)-2]
} // }
s = strings.TrimSuffix(s, "()")
parts = strings.SplitN(s, ".", 2) parts = strings.SplitN(s, ".", 2)
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil { if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
return return
@@ -311,7 +312,6 @@ func divAnyFract(af1, af2 any) (quot any, err error) {
if f2.num == 0 { if f2.num == 0 {
err = errors.New("division by zero") err = errors.New("division by zero")
return return
return
} }
if f1.num == 0 || f2.den == 0 { if f1.num == 0 || f2.den == 0 {
quot = 0 quot = 0
+169 -40
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 {
@@ -112,25 +113,24 @@ type funcInfo struct {
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
var minArgs = 0 var minArgs = 0
var maxArgs = 0 var maxArgs = 0
if params != nil { for _, p := range params {
for _, p := range params { if maxArgs == -1 {
if maxArgs == -1 { return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) }
} if p.IsDefault() || p.IsOptional() {
if p.IsDefault() || p.IsOptional() { maxArgs++
maxArgs++ } else if maxArgs == minArgs {
} else if maxArgs == minArgs { minArgs++
minArgs++ maxArgs++
maxArgs++ } else {
} else { return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name()) }
} if p.IsRepeat() {
if p.IsRepeat() { minArgs--
minArgs-- maxArgs = -1
maxArgs = -1
}
} }
} }
info = &funcInfo{ info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params, name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
} }
@@ -138,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
} }
@@ -217,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
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
} }
for i := passedCount; i < len(info.formalParams); i++ { for i, tree := range callTerm.children {
p := info.formalParams[i] var paramValue any
if !p.IsDefault() { 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 break
} }
*varActualParams = append(*varActualParams, p.DefaultValue())
} }
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) { if err == nil {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams)) if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
} }
if err == nil { if passedCount < len(info.formalParams) {
ctx = info.AllocContext(parentCtx) for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
} }
return 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 { }
functor := info.Functor() return
result, err = functor.Invoke(ctx, name, actualParams) }
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
actualParams = bindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx) exportObjectsToParent(ctx)
} }
} else { } else {
@@ -255,3 +347,40 @@ func CallFunction(parentCtx ExprContext, name string, actualParams []any) (resul
} }
return return
} }
func CallFunctionByParams(parentCtx ExprContext, name string, params 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
+14 -6
View File
@@ -25,13 +25,13 @@ func checkStringParamExpected(funcName string, paramValue any, paramPos int) (er
return return
} }
func addSourceEnvImportDirs(varName string, dirList []string) []string { // func addSourceEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
} // }
func addPluginEnvImportDirs(varName string, dirList []string) []string { // func addPluginEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
} // }
func addEnvImportDirs(envVarName string, dirList []string) []string { func addEnvImportDirs(envVarName string, dirList []string) []string {
if dirSpec, exists := os.LookupEnv(envVarName); exists { if dirSpec, exists := os.LookupEnv(envVarName); exists {
@@ -75,7 +75,11 @@ func isFile(filePath string) bool {
} }
func searchAmongPath(filename string, dirList []string) (filePath string) { func searchAmongPath(filename string, dirList []string) (filePath string) {
var err error
for _, dir := range dirList { for _, dir := range dirList {
if dir, err = ExpandPath(dir); err != nil {
continue
}
if fullPath := path.Join(dir, filename); isFile(fullPath) { if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath filePath = fullPath
break break
@@ -90,6 +94,10 @@ func isPathRelative(filePath string) bool {
} }
func makeFilepath(filename string, dirList []string) (filePath string, err error) { func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if filename, err = ExpandPath(filename); err != nil {
return
}
if path.IsAbs(filename) || isPathRelative(filename) { if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) { if isFile(filename) {
filePath = filename filePath = filename
+12 -8
View File
@@ -12,25 +12,29 @@ import (
// Operator names // Operator names
const ( const (
initName = "init" InitName = "init"
cleanName = "clean" CleanName = "clean"
resetName = "reset" ResetName = "reset"
nextName = "next" NextName = "next"
currentName = "current" CurrentName = "current"
indexName = "index" IndexName = "index"
countName = "count" CountName = "count"
FilterName = "filter"
MapName = "map"
) )
type Iterator interface { type Iterator interface {
Typer
Next() (item any, err error) // must return io.EOF after the last item Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error) Current() (item any, err error)
Index() int Index() int
Count() int
} }
type ExtIterator interface { type ExtIterator interface {
Iterator Iterator
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 {
+20 -7
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// iter-list.go // list-iterator.go
package expr package expr
import ( import (
@@ -85,18 +85,26 @@ func (it *ListIterator) String() string {
return fmt.Sprintf("$(#%d)", l) return fmt.Sprintf("$(#%d)", l)
} }
func (it *ListIterator) TypeName() string {
return "ListIterator"
}
func (it *ListIterator) HasOperation(name string) bool { func (it *ListIterator) HasOperation(name string) bool {
yes := name == resetName || name == indexName || name == countName yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
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 resetName: case NextName:
v, err = it.Next()
case ResetName:
v, err = it.Reset() v, err = it.Reset()
case indexName: case IndexName:
v = int64(it.Index()) v = int64(it.Index())
case countName: case CurrentName:
v, err = it.Current()
case CountName:
v = it.count v = it.count
default: default:
err = errNoOperation(name) err = errNoOperation(name)
@@ -135,7 +143,12 @@ func (it *ListIterator) Index() int {
return it.index return it.index
} }
func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (bool, error) { func (it *ListIterator) Reset() (bool, error) {
it.index = it.start it.index = it.start - it.step
it.count = 0
return true, nil return true, nil
} }
+14 -13
View File
@@ -26,9 +26,10 @@ func newList(listAny []any) (list *ListType) {
func NewList(listAny []any) (list *ListType) { func NewList(listAny []any) (list *ListType) {
if listAny != nil { if listAny != nil {
ls := make(ListType, len(listAny)) ls := make(ListType, len(listAny))
for i, item := range listAny { // for i, item := range listAny {
ls[i] = item // ls[i] = item
} // }
copy(ls, listAny)
list = &ls list = &ls
} }
return return
@@ -105,16 +106,16 @@ func (ls *ListType) TypeName() string {
return "list" return "list"
} }
func (list *ListType) indexDeepCmp(target any) (index int) { // func (list *ListType) indexDeepCmp(target any) (index int) {
index = -1 // index = -1
for i, item := range *list { // for i, item := range *list {
if reflect.DeepEqual(item, target) { // if reflect.DeepEqual(item, target) {
index = i // index = i
break // break
} // }
} // }
return // return
} // }
func (ls *ListType) contains(t *ListType) (answer bool) { func (ls *ListType) contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) { if len(*ls) >= len(*t) {
+2 -2
View File
@@ -18,8 +18,8 @@ func newDictTerm(args map[any]*term) *term {
} }
// -------- dict func // -------- dict func
func evalDict(ctx ExprContext, self *term) (v any, err error) { func evalDict(ctx ExprContext, opTerm *term) (v any, err error) {
dict, _ := self.value().(map[any]*term) dict, _ := opTerm.value().(map[any]*term)
items := make(DictType, len(dict)) items := make(DictType, len(dict))
for key, tree := range dict { for key, tree := range dict {
var param any var param any
+3 -3
View File
@@ -20,11 +20,11 @@ func newExprTerm(root *term) *term {
} }
// -------- eval expr // -------- eval expr
func evalExpr(ctx ExprContext, self *term) (v any, err error) { func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) {
if expr, ok := self.value().(*term); ok { if expr, ok := opTerm.value().(*term); ok {
v, err = expr.compute(ctx) v, err = expr.compute(ctx)
} else { } else {
err = fmt.Errorf("expression expected, got %T", self.value()) err = fmt.Errorf("expression expected, got %T", opTerm.value())
} }
return return
} }
+23 -17
View File
@@ -21,20 +21,26 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
} }
// -------- eval func call // -------- eval func call
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) { // func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := self.tk.Value.(string) // name, _ := opTerm.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5) // params := make([]any, len(opTerm.children), len(opTerm.children)+5)
for i, tree := range self.children { // for i, tree := range opTerm.children {
var param any // var param any
if param, err = tree.compute(ctx); err != nil { // if param, err = tree.compute(ctx); err != nil {
break // break
} // }
params[i] = param // params[i] = param
} // }
if err == nil { // if err == nil {
v, err = CallFunction(ctx, name, params) // v, err = CallFunction(ctx, name, params)
} // }
// return
// }
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := opTerm.tk.Value.(string)
v, err = CallFunctionByTerm(ctx, name, opTerm)
return return
} }
@@ -51,11 +57,11 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
} }
// -------- eval func def // -------- eval func def
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) {
bodySpec := self.value() bodySpec := opTerm.value()
if expr, ok := bodySpec.(*ast); ok { if expr, ok := bodySpec.(*ast); ok {
paramList := make([]ExprFuncParam, 0, len(self.children)) paramList := make([]ExprFuncParam, 0, len(opTerm.children))
for _, param := range self.children { for _, param := range opTerm.children {
var defValue any var defValue any
flags := paramFlags(0) flags := paramFlags(0)
if len(param.children) > 0 { if len(param.children) > 0 {
+28 -47
View File
@@ -5,29 +5,12 @@
package expr package expr
import ( import (
"fmt"
"slices" "slices"
"strings" "strings"
) )
// -------- 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{
@@ -42,9 +25,9 @@ func newIteratorTerm(tk *Token, args []*term) *term {
// -------- eval iterator // -------- eval iterator
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
values = make([]any, len(a)) values = make([]any, len(terms))
for i, t := range a { for i, t := range terms {
var value any var value any
if value, err = t.compute(ctx); err == nil { if value, err = t.compute(ctx); err == nil {
values[i] = value values[i] = value
@@ -55,26 +38,24 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
return return
} }
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) { func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) {
if len(self.children) < 1 || self.children[0] == nil { if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
err = self.Errorf("missing the data-source parameter") err = iteratorTerm.Errorf("missing the data-source parameter")
return return
} }
value, err = self.children[0].compute(ctx) value, err = iteratorTerm.children[0].compute(ctx)
return return
} }
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
// if dictAny, ok := firstChildValue.(map[any]any); ok {
if dictAny, ok := firstChildValue.(*DictType); ok { if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{currentName, nextName} requiredFields := []string{NextName}
fieldsMask := 0b11 fieldsMask := 0b1
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { if key, ok := keyAny.(string); ok {
//if functor, ok := item.(*funcDefFunctor); ok {
if functor, ok := item.(Functor); ok { if functor, ok := item.(Functor); ok {
ds[key] = functor ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 { if index := slices.Index(requiredFields, key); index >= 0 {
@@ -91,57 +72,57 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
missingFields = append(missingFields, field) missingFields = append(missingFields, field)
} }
} }
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} }
return return
} }
func evalIterator(ctx ExprContext, self *term) (v any, err error) { func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
var firstChildValue any var firstChildValue any
var ds map[string]Functor var ds map[string]Functor
if firstChildValue, err = evalFirstChild(ctx, self); err != nil { if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
return return
} }
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil { if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
return return
} }
if ds != nil { if ds != nil {
dc := newDataCursor(ctx, ds) var dc *dataCursor
if initFunc, exists := ds[initName]; exists && initFunc != nil { if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any var args []any
if len(self.children) > 1 { var resource any
if args, err = evalTermArray(ctx, self.children[1:]); err != nil { if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
return return
} }
} else { } else {
args = []any{} args = []any{}
} }
initCtx := dc.ctx.Clone() actualParams := bindActualParams(initFunc, args)
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
initCtx := ctx.Clone()
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
return return
} }
exportObjects(dc.ctx, initCtx) dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} }
dc.nextFunc, _ = ds[nextName]
dc.currentFunc, _ = ds[currentName]
dc.cleanFunc, _ = ds[cleanName]
dc.resetFunc, _ = ds[resetName]
v = dc v = dc
} else if list, ok := firstChildValue.(*ListType); ok { } else if list, ok := firstChildValue.(*ListType); ok {
var args []any var args []any
if args, err = evalSibling(ctx, self.children, nil); err == nil { if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
v = NewListIterator(list, args) v = NewListIterator(list, args)
} }
} else { } else {
var list []any var list []any
if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil { if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
v = NewArrayIterator(list) v = NewArrayIterator(list)
} }
} }
+2 -2
View File
@@ -21,8 +21,8 @@ func newListTerm(row, col int, args []*term) *term {
} }
// -------- list func // -------- list func
func evalList(ctx ExprContext, self *term) (v any, err error) { func evalList(ctx ExprContext, opTerm *term) (v any, err error) {
list, _ := self.value().([]*term) list, _ := opTerm.value().([]*term)
items := make(ListType, len(list)) items := make(ListType, len(list))
for i, tree := range list { for i, tree := range list {
var param any var param any
+2 -2
View File
@@ -17,8 +17,8 @@ func newLiteralTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalLiteral(ctx ExprContext, self *term) (v any, err error) { func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) {
v = self.tk.Value v = opTerm.tk.Value
return return
} }
+3 -3
View File
@@ -41,10 +41,10 @@ func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
} }
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx ExprContext, self *term) (v any, err error) { func evalSelectorCase(ctx ExprContext, opTerm *term) (v any, err error) {
var ok bool var ok bool
if v, ok = self.value().(*selectorCase); !ok { if v, ok = opTerm.value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", self.value()) err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
} }
return return
} }
+3 -3
View File
@@ -21,11 +21,11 @@ func newVarTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalVar(ctx ExprContext, self *term) (v any, err error) { func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
var exists bool var exists bool
name := self.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)
+15 -11
View File
@@ -60,30 +60,34 @@ func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) {
return return
} }
func evalAssign(ctx ExprContext, self *term) (v any, err error) { func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
leftTerm := self.children[0] leftTerm := opTerm.children[0]
leftSym := leftTerm.symbol() leftSym := leftTerm.symbol()
if leftSym != SymVariable && leftSym != SymIndex { if leftSym != SymVariable && leftSym != SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", self.tk.source) err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
return return
} }
rightChild := self.children[1] rightChild := opTerm.children[1]
if v, err = rightChild.compute(ctx); err == nil { if v, err = rightChild.compute(ctx); err == nil {
if functor, ok := v.(Functor); ok { if functor, ok := v.(Functor); ok {
if info := functor.GetFunc(); info != nil { if leftSym == SymVariable {
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) if info := functor.GetFunc(); info != nil {
} else if funcDef, ok := functor.(*exprFunctor); ok { ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) } else if funcDef, ok := functor.(*exprFunctor); ok {
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs) ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs)
} else {
err = opTerm.Errorf("unknown function %s()", rightChild.source())
}
} else { } else {
err = self.Errorf("unknown function %s()", rightChild.source()) err = assignValue(ctx, leftTerm, v)
} }
} else { } else {
err = assignValue(ctx, leftTerm, v) err = assignValue(ctx, leftTerm, v)
+3 -3
View File
@@ -18,17 +18,17 @@ func newNotTerm(tk *Token) (inst *term) {
} }
} }
func evalNot(ctx ExprContext, self *term) (v any, err error) { func evalNot(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if b, ok := ToBool(rightValue); ok { if b, ok := ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatibleType(rightValue)
} }
return return
} }
+4 -4
View File
@@ -18,10 +18,10 @@ func newBuiltinTerm(tk *Token) (inst *term) {
} }
} }
func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
if ImportInContext(module) { if ImportInContext(module) {
count++ count++
} else { } else {
err = self.Errorf("unknown builtin module %q", module) err = opTerm.Errorf("unknown builtin module %q", module)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }
+2 -2
View File
@@ -16,8 +16,8 @@ func newButTerm(tk *Token) (inst *term) {
} }
} }
func evalBut(ctx ExprContext, self *term) (v any, err error) { func evalBut(ctx ExprContext, opTerm *term) (v any, err error) {
_, v, err = self.evalInfix(ctx) _, v, err = opTerm.evalInfix(ctx)
return return
} }
-85
View File
@@ -1,85 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-coalesce.go
package expr
//-------- null coalesce term
func newNullCoalesceTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalNullCoalesce,
}
}
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- coalesce assign term
func newCoalesceAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAssignCoalesce,
}
}
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
}
+5 -5
View File
@@ -16,15 +16,15 @@ func newContextTerm(tk *Token) (inst *term) {
} }
} }
func evalContextValue(ctx ExprContext, self *term) (v any, err error) { func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var sourceCtx ExprContext var sourceCtx ExprContext
if self.children == nil || len(self.children) == 0 { if opTerm.children == nil || len(opTerm.children) == 0 {
sourceCtx = ctx sourceCtx = ctx
} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" { } else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
sourceCtx = globalCtx sourceCtx = globalCtx
} else if childValue, err = self.evalPrefix(ctx); err == nil { } else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
if dc, ok := childValue.(*dataCursor); ok { if dc, ok := childValue.(*dataCursor); ok {
sourceCtx = dc.ctx sourceCtx = dc.ctx
} }
@@ -49,7 +49,7 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
v = d v = d
} }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
+1 -1
View File
@@ -16,7 +16,7 @@ func newExportAllTerm(tk *Token) (inst *term) {
} }
} }
func evalExportAll(ctx ExprContext, self *term) (v any, err error) { func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, control_export_all)
return return
} }
+124
View File
@@ -0,0 +1,124 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-default.go
package expr
//-------- default term
func newDefaultTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalDefault,
}
}
func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- alternate term
func newAlternateTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAlternate,
}
}
func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
v = rightValue
}
} else {
v = leftValue
}
return
}
//-------- default assign term
func newDefaultAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAssignDefault,
}
}
func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
}
+7 -7
View File
@@ -15,24 +15,24 @@ func newDotTerm(tk *Token) (inst *term) {
} }
} }
func evalDot(ctx ExprContext, self *term) (v any, err error) { func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case ExtIterator: case ExtIterator:
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
@@ -41,8 +41,8 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
err = indexTerm.tk.ErrorExpectedGot("identifier") err = indexTerm.tk.ErrorExpectedGot("identifier")
} }
default: default:
if rightValue, err = self.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} }
return return
+3 -3
View File
@@ -18,10 +18,10 @@ func newFactTerm(tk *Token) (inst *term) {
} }
} }
func evalFact(ctx ExprContext, self *term) (v any, err error) { func evalFact(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue any var leftValue any
if leftValue, err = self.evalPrefix(ctx); err != nil { if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -36,7 +36,7 @@ func evalFact(ctx ExprContext, self *term) (v any, err error) {
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i) err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
} }
} else { } else {
err = self.errIncompatibleType(leftValue) err = opTerm.errIncompatibleType(leftValue)
} }
return return
} }
+2 -2
View File
@@ -24,12 +24,12 @@ func newFractionTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalFraction(ctx ExprContext, self *term) (v any, err error) { func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
var numValue, denValue any var numValue, denValue any
var num, den int64 var num, den int64
var ok bool var ok bool
if numValue, denValue, err = self.evalInfix(ctx); err != nil { if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if num, ok = numValue.(int64); !ok { if num, ok = numValue.(int64); !ok {
+3 -3
View File
@@ -21,10 +21,10 @@ func newInTerm(tk *Token) (inst *term) {
// return // return
// } // }
func evalIn(ctx ExprContext, self *term) (v any, err error) { func evalIn(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -35,7 +35,7 @@ func evalIn(ctx ExprContext, self *term) (v any, err error) {
dict, _ := rightValue.(*DictType) dict, _ := rightValue.(*DictType)
v = dict.hasKey(leftValue) v = dict.hasKey(leftValue)
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+13 -10
View File
@@ -16,37 +16,40 @@ func newIncludeTerm(tk *Token) (inst *term) {
} }
} }
func evalInclude(ctx ExprContext, self *term) (v any, err error) { func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsList(childValue) { if IsList(childValue) {
list, _ := childValue.([]any) list, _ := childValue.(*ListType)
for i, filePathSpec := range list { for i, filePathSpec := range *list {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
} else { } else {
err = self.Errorf("can't load file %q", filePath) err = opTerm.Errorf("can't load file %q", filePath)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec) err = opTerm.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
break break
} }
} }
} else if IsString(childValue) { } else if IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
v, err = EvalFile(ctx, filePath) if v, err = EvalFile(ctx, filePath); err == nil {
count++
}
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
if err == nil { if err != nil {
v = count //v = count
v = nil
} }
return return
} }
+8 -23
View File
@@ -15,7 +15,7 @@ func newIndexTerm(tk *Token) (inst *term) {
} }
} }
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) { func verifyKey(indexList *ListType) (index any, err error) {
index = (*indexList)[0] index = (*indexList)[0]
return return
} }
@@ -59,18 +59,18 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
return return
} }
func evalIndex(ctx ExprContext, self *term) (v any, err error) { func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var indexList *ListType var indexList *ListType
var ok bool var ok bool
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
if indexList, ok = rightValue.(*ListType); !ok { if indexList, ok = rightValue.(*ListType); !ok {
err = self.Errorf("invalid index expression") err = opTerm.Errorf("invalid index expression")
return return
} else if len(*indexList) != 1 { } else if len(*indexList) != 1 {
err = indexTerm.Errorf("one index only is allowed") err = indexTerm.Errorf("one index only is allowed")
@@ -90,16 +90,9 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case *DictType: case *DictType:
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*unboxedValue)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
} */
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue) v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if isIntPair((*indexList)[0]) { } else if isIntPair((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
@@ -115,18 +108,10 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = unboxedValue[start:end] v = unboxedValue[start:end]
} }
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if IsDict(leftValue) { } else if IsDict(leftValue) {
d := leftValue.(*DictType) d := leftValue.(*DictType)
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}*/
v, err = getDictItem(d, indexTerm, indexList, rightValue) v, err = getDictItem(d, indexTerm, indexList, rightValue)
} }
return return
@@ -136,7 +121,7 @@ func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue a
var ok bool var ok bool
var indexValue any var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if indexValue, err = verifyKey(indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok { if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
} }
+10 -10
View File
@@ -26,10 +26,10 @@ func newAppendTerm(tk *Token) (inst *term) {
} }
} }
func evalInsert(ctx ExprContext, self *term) (v any, err error) { func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -37,19 +37,19 @@ func evalInsert(ctx ExprContext, self *term) (v any, err error) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*ListType)
newList := append(ListType{leftValue}, *list...) newList := append(ListType{leftValue}, *list...)
v = &newList v = &newList
if self.children[1].symbol() == SymVariable { if opTerm.children[1].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[1].source(), v) ctx.UnsafeSetVar(opTerm.children[1].source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalAppend(ctx ExprContext, self *term) (v any, err error) { func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -57,11 +57,11 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
if self.children[0].symbol() == SymVariable { if opTerm.children[0].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[0].source(), v) ctx.UnsafeSetVar(opTerm.children[0].source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+5 -4
View File
@@ -16,22 +16,23 @@ func newIterValueTerm(tk *Token) (inst *term) {
} }
} }
func evalIterValue(ctx ExprContext, self *term) (v any, err error) { func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Current() v, err = it.Current()
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymOpenClosedRound, newIterValueTerm) // registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymCaret, newIterValueTerm)
} }
+10 -10
View File
@@ -16,10 +16,10 @@ func newLengthTerm(tk *Token) (inst *term) {
} }
} }
func evalLength(ctx ExprContext, self *term) (v any, err error) { func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -30,18 +30,18 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if IsDict(childValue) { } else if IsDict(childValue) {
// m, _ := childValue.(map[any]any)
m, _ := childValue.(*DictType) m, _ := childValue.(*DictType)
v = int64(len(*m)) v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { v = int64(it.Count())
count, _ := extIt.CallOperation(countName, nil) // if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
v, _ = ToGoInt(count, "") // count, _ := extIt.CallOperation(CountName, nil)
} else { // v, _ = ToGoInt(count, "")
v = int64(it.Index() + 1) // } else {
} // v = int64(it.Index() + 1)
// }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
+4 -26
View File
@@ -4,10 +4,6 @@
// operator-plugin.go // operator-plugin.go
package expr package expr
import (
"io"
)
//-------- plugin term //-------- plugin term
func newPluginTerm(tk *Token) (inst *term) { func newPluginTerm(tk *Token) (inst *term) {
@@ -20,33 +16,15 @@ func newPluginTerm(tk *Token) (inst *term) {
} }
} }
func evalPlugin(ctx ExprContext, self *term) (v any, err error) { func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var moduleSpec any var count int
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) if count, err = importPluginFromSearchPath(childValue); err == nil {
count := 0
it := NewAnyIterator(childValue)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil {
break
}
count++
} else {
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
if err == nil {
v = int64(count) v = int64(count)
} }
return return
+37 -5
View File
@@ -17,20 +17,51 @@ func newPostIncTerm(tk *Token) *term {
} }
} }
func evalPostInc(ctx ExprContext, self *term) (v any, err error) { func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else if IsInteger(childValue) && self.children[0].symbol() == SymVariable { } else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(self.children[0].source(), i+1) ctx.SetVar(opTerm.children[0].source(), i+1)
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
}
return
}
// -------- post decrement term
func newPostDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostDec,
}
}
func evalPostDec(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
/* if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else */if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i-1)
} else {
err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
@@ -38,4 +69,5 @@ func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm) registerTermConstructor(SymDoublePlus, newPostIncTerm)
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
} }
+14 -20
View File
@@ -14,8 +14,6 @@ import (
func newMultiplyTerm(tk *Token) (inst *term) { func newMultiplyTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -23,10 +21,10 @@ func newMultiplyTerm(tk *Token) (inst *term) {
} }
} }
func evalMultiply(ctx ExprContext, self *term) (v any, err error) { func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -45,7 +43,7 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
v = leftInt * rightInt v = leftInt * rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -55,8 +53,6 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
func newDivideTerm(tk *Token) (inst *term) { func newDivideTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -64,10 +60,10 @@ func newDivideTerm(tk *Token) (inst *term) {
} }
} }
func evalDivide(ctx ExprContext, self *term) (v any, err error) { func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -90,7 +86,7 @@ func evalDivide(ctx ExprContext, self *term) (v any, err error) {
} }
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -107,10 +103,10 @@ func newDivideAsFloatTerm(tk *Token) (inst *term) {
} }
} }
func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) { func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -122,18 +118,16 @@ func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) {
v = numAsFloat(leftValue) / d v = numAsFloat(leftValue) / d
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
//-------- reminder term //-------- reminder term
func newReminderTerm(tk *Token) (inst *term) { func newRemainderTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -141,10 +135,10 @@ func newReminderTerm(tk *Token) (inst *term) {
} }
} }
func evalReminder(ctx ExprContext, self *term) (v any, err error) { func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -157,7 +151,7 @@ func evalReminder(ctx ExprContext, self *term) (v any, err error) {
v = leftInt % rightInt v = leftInt % rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -167,5 +161,5 @@ func init() {
registerTermConstructor(SymStar, newMultiplyTerm) registerTermConstructor(SymStar, newMultiplyTerm)
registerTermConstructor(SymSlash, newDivideTerm) registerTermConstructor(SymSlash, newDivideTerm)
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm) registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
registerTermConstructor(SymPercent, newReminderTerm) registerTermConstructor(SymPercent, newRemainderTerm)
} }
+6 -6
View File
@@ -34,25 +34,25 @@ func newRangeTerm(tk *Token) (inst *term) {
} }
} }
func evalRange(ctx ExprContext, self *term) (v any, err error) { func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
// if err = self.checkOperands(); err != nil { // if err = self.checkOperands(); err != nil {
// return // return
// } // }
if len(self.children) == 0 { if len(opTerm.children) == 0 {
leftValue = int64(0) leftValue = int64(0)
rightValue = int64(-1) rightValue = int64(-1)
} else if len(self.children) == 1 { } else if len(opTerm.children) == 1 {
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return return
} }
rightValue = int64(ConstLastIndex) rightValue = int64(ConstLastIndex)
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { } else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if !(IsInteger(leftValue) && IsInteger(rightValue)) { if !(IsInteger(leftValue) && IsInteger(rightValue)) {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
return return
} }
+16 -16
View File
@@ -45,10 +45,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func evalEqual(ctx ExprContext, self *term) (v any, err error) { func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -68,8 +68,8 @@ func newNotEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) { func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) {
if v, err = evalEqual(ctx, self); err == nil { if v, err = evalEqual(ctx, opTerm); err == nil {
b, _ := ToBool(v) b, _ := ToBool(v)
v = !b v = !b
} }
@@ -119,13 +119,13 @@ func lessThan(self *term, a, b any) (isLess bool, err error) {
return return
} }
func evalLess(ctx ExprContext, self *term) (v any, err error) { func evalLess(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, leftValue, rightValue) v, err = lessThan(opTerm, leftValue, rightValue)
return return
} }
@@ -154,14 +154,14 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
return return
} }
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) { func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, leftValue, rightValue) v, err = lessThanOrEqual(opTerm, leftValue, rightValue)
return return
} }
@@ -178,14 +178,14 @@ func newGreaterTerm(tk *Token) (inst *term) {
} }
} }
func evalGreater(ctx ExprContext, self *term) (v any, err error) { func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, rightValue, leftValue) v, err = lessThan(opTerm, rightValue, leftValue)
return return
} }
@@ -201,14 +201,14 @@ func newGreaterEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) { func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, rightValue, leftValue) v, err = lessThanOrEqual(opTerm, rightValue, leftValue)
return return
} }
+4 -4
View File
@@ -39,19 +39,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
return return
} }
func evalSelector(ctx ExprContext, self *term) (v any, err error) { func evalSelector(ctx ExprContext, opTerm *term) (v any, err error) {
var exprValue any var exprValue any
var match bool var match bool
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
exprTerm := self.children[0] exprTerm := opTerm.children[0]
if exprValue, err = exprTerm.compute(ctx); err != nil { if exprValue, err = exprTerm.compute(ctx); err != nil {
return return
} }
caseListTerm := self.children[1] caseListTerm := opTerm.children[1]
caseList, _ := caseListTerm.value().([]*term) caseList, _ := caseListTerm.value().([]*term)
for i, caseTerm := range caseList { for i, caseTerm := range caseList {
caseSel := caseTerm.value() caseSel := caseTerm.value()
+5 -5
View File
@@ -28,29 +28,29 @@ func newMinusSignTerm(tk *Token) (inst *term) {
} }
} }
func evalSign(ctx ExprContext, self *term) (v any, err error) { func evalSign(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if IsFloat(rightValue) { if IsFloat(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if IsInteger(rightValue) { } else if IsInteger(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
} else { } else {
v = rightValue v = rightValue
} }
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatibleType(rightValue)
} }
return return
} }
+8 -12
View File
@@ -21,10 +21,10 @@ func newPlusTerm(tk *Token) (inst *term) {
} }
} }
func evalPlus(ctx ExprContext, self *term) (v any, err error) { func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -44,12 +44,8 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
rightList, _ = rightValue.(*ListType) rightList, _ = rightValue.(*ListType)
sumList := make(ListType, 0, len(*leftList)+len(*rightList)) sumList := make(ListType, 0, len(*leftList)+len(*rightList))
for _, item := range *leftList { sumList = append(sumList, *leftList...)
sumList = append(sumList, item) sumList = append(sumList, *rightList...)
}
for _, item := range *rightList {
sumList = append(sumList, item)
}
v = &sumList v = &sumList
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) { } else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
if IsFloat(leftValue) || IsFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
@@ -64,7 +60,7 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
c.merge(rightDict) c.merge(rightDict)
v = c v = c
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -81,10 +77,10 @@ func newMinusTerm(tk *Token) (inst *term) {
} }
} }
func evalMinus(ctx ExprContext, self *term) (v any, err error) { func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -109,7 +105,7 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
} }
v = &diffList v = &diffList
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = minusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-unset.go
package expr
import "strings"
//-------- unset term
func newUnsetTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priSign,
evalFunc: evalUnset,
}
}
func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, err error) {
if name, ok := item.(string); ok {
var size int
if strings.HasSuffix(name, "()") {
size = ctx.FuncCount()
ctx.DeleteFunc(strings.TrimRight(name, "()"))
deleted = ctx.FuncCount() < size
} else {
size = ctx.VarCount()
ctx.DeleteVar(name)
deleted = ctx.VarCount() < size
}
} else {
err = opTerm.errIncompatibleType(item)
}
return
}
func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
var deleted bool
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
count := 0
if IsList(childValue) {
list, _ := childValue.(*ListType)
for _, item := range *list {
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
break
} else if deleted {
count++
}
}
} else if deleted, err = deleteContextItem(ctx, opTerm, childValue); err == nil && deleted {
count++
}
if err == nil {
v = int64(count)
}
return
}
// init
func init() {
registerTermConstructor(SymKwUnset, newUnsetTerm)
}
+104 -48
View File
@@ -18,13 +18,19 @@ func NewParser() (p *parser) {
return p return p
} }
func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) { func (parser *parser) Next(scanner *scanner) (tk *Token) {
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() {
}
return
}
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10) args := make([]*term, 0, 10)
itemExpected := false itemExpected := false
lastSym := SymUnknown lastSym := SymUnknown
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast var subTree *ast
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
break break
} }
prev := scanner.Previous() prev := scanner.Previous()
@@ -48,7 +54,7 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
return return
} }
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
// Example: "add = func(x,y) {x+y} // Example: "add = func(x,y) {x+y}
var body *ast var body *ast
args := make([]*term, 0) args := make([]*term, 0)
@@ -57,15 +63,15 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
itemExpected := false itemExpected := false
tk := scanner.Previous() tk := scanner.Previous()
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
tk = scanner.Next() tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) { if tk.IsSymbol(SymIdentifier) {
param := newTerm(tk) param := newTerm(tk)
args = append(args, param) args = append(args, param)
tk = scanner.Next() tk = parser.Next(scanner)
if tk.Sym == SymEqual { if tk.Sym == SymEqual {
var paramExpr *ast var paramExpr *ast
defaultParamsStarted = true defaultParamsStarted = true
if paramExpr, err = self.parseItem(scanner, false, SymComma, SymClosedRound); err != nil { if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
break break
} }
param.forceChild(paramExpr.root) param.forceChild(paramExpr.root)
@@ -86,9 +92,9 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
err = tk.ErrorExpectedGot(")") err = tk.ErrorExpectedGot(")")
} }
if err == nil { if err == nil {
tk = scanner.Next() tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) { if tk.IsSymbol(SymOpenBrace) {
body, err = self.parseGeneral(scanner, true, true, SymClosedBrace) body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
} else { } else {
err = tk.ErrorExpectedGot("{") err = tk.ErrorExpectedGot("{")
} }
@@ -104,7 +110,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
return return
} }
func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
r, c := scanner.lastPos() r, c := scanner.lastPos()
args := make([]*term, 0) args := make([]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
@@ -112,7 +118,7 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
for lastSym != SymClosedSquare && lastSym != SymEos { for lastSym != SymClosedSquare && lastSym != SymEos {
var subTree *ast var subTree *ast
zeroRequired := scanner.current.Sym == SymColon zeroRequired := scanner.current.Sym == SymColon
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
root := subTree.root root := subTree.root
if root != nil { if root != nil {
if !parsingIndeces && root.symbol() == SymColon { if !parsingIndeces && root.symbol() == SymColon {
@@ -153,14 +159,14 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
return return
} }
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
tk := scanner.Previous() tk := scanner.Previous()
args := make([]*term, 0) args := make([]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast var subTree *ast
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
if subTree.root != nil { if subTree.root != nil {
args = append(args, subTree.root) args = append(args, subTree.root)
} else if itemExpected { } else if itemExpected {
@@ -184,8 +190,8 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t
return return
} }
func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) { func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
tk := scanner.Next() tk := parser.Next(scanner)
if tk.Sym == SymError { if tk.Sym == SymError {
err = tk.Error() err = tk.Error()
return return
@@ -194,7 +200,7 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
return return
} }
if tk.Sym == SymInteger || tk.Sym == SymString { if tk.Sym == SymInteger || tk.Sym == SymString {
tkSep := scanner.Next() tkSep := parser.Next(scanner)
if tkSep.Sym != SymColon { if tkSep.Sym != SymColon {
err = tkSep.ErrorExpectedGot(":") err = tkSep.ErrorExpectedGot(":")
} else { } else {
@@ -206,14 +212,14 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
return return
} }
func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
args := make(map[any]*term, 0) args := make(map[any]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedBrace && lastSym != SymEos { for lastSym != SymClosedBrace && lastSym != SymEos {
var subTree *ast var subTree *ast
var key any var key any
if key, err = self.parseDictKey(scanner, allowVarRef); err != nil { if key, err = parser.parseDictKey(scanner); err != nil {
break break
} else if key == nil { } else if key == nil {
tk := scanner.Previous() tk := scanner.Previous()
@@ -223,10 +229,10 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
} }
break break
} }
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
if subTree.root != nil { if subTree.root != nil {
args[key] = subTree.root args[key] = subTree.root
} else if key != nil { } else /*if key != nil*/ {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("dictionary-value") err = prev.ErrorExpectedGot("dictionary-value")
break break
@@ -248,10 +254,10 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
return return
} }
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) { func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term var filterList *term
var caseExpr *ast var caseExpr *ast
tk := scanner.Next() tk := parser.Next(scanner)
startRow := tk.row startRow := tk.row
startCol := tk.col startCol := tk.col
if tk.Sym == SymOpenSquare { if tk.Sym == SymOpenSquare {
@@ -259,10 +265,10 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
err = tk.Errorf("case list in default clause") err = tk.Errorf("case list in default clause")
return return
} }
if filterList, err = self.parseList(scanner, false, allowVarRef); err != nil { if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
return return
} }
tk = scanner.Next() tk = parser.Next(scanner)
startRow = tk.row startRow = tk.row
startCol = tk.col startCol = tk.col
} else if !defaultCase { } else if !defaultCase {
@@ -270,7 +276,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
} }
if tk.Sym == SymOpenBrace { if tk.Sym == SymOpenBrace {
if caseExpr, err = self.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil { if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
return return
} }
} else { } else {
@@ -296,25 +302,25 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
caseTerm.parent = selectorTerm caseTerm.parent = selectorTerm
} }
func (self *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) { func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
var caseTerm *term var caseTerm *term
tk := scanner.makeToken(SymSelector, '?') tk := scanner.makeToken(SymSelector, '?')
if selectorTerm, err = tree.addToken2(tk); err != nil { if selectorTerm, err = tree.addToken(tk); err != nil {
return return
} }
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, false); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
} }
return return
} }
func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, false, allowVarRef, termSymbols...) return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
} }
func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, true, false, termSymbols...) return parser.parseGeneral(scanner, true, false, termSymbols...)
} }
func couldBeACollection(t *term) bool { func couldBeACollection(t *term) bool {
@@ -333,17 +339,18 @@ func couldBeACollection(t *term) bool {
// return areOut // return areOut
// } // }
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
var selectorTerm *term = nil var selectorTerm *term = nil
var currentTerm *term = nil var currentTerm *term = nil
var tk *Token var tk *Token
tree = NewAst() tree = NewAst()
firstToken := true firstToken := true
// lastSym := SymUnknown // lastSym := SymUnknown
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() { for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
if tk.Sym == SymComment { // if tk.Sym == SymComment {
continue // continue
} // }
if tk.Sym == SymSemiColon { if tk.Sym == SymSemiColon {
if allowForest { if allowForest {
@@ -371,21 +378,21 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
switch tk.Sym { switch tk.Sym {
case SymOpenRound: case SymOpenRound:
var subTree *ast var subTree *ast
if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil { if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
subTree.root.priority = priValue subTree.root.priority = priValue
err = tree.addTerm(newExprTerm(subTree.root)) err = tree.addTerm(newExprTerm(subTree.root))
currentTerm = subTree.root currentTerm = subTree.root
} }
case SymFuncCall: case SymFuncCall:
var funcCallTerm *term var funcCallTerm *term
if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil { if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
err = tree.addTerm(funcCallTerm) err = tree.addTerm(funcCallTerm)
currentTerm = funcCallTerm currentTerm = funcCallTerm
} }
case SymOpenSquare: case SymOpenSquare:
var listTerm *term var listTerm *term
parsingIndeces := couldBeACollection(currentTerm) parsingIndeces := couldBeACollection(currentTerm)
if listTerm, err = self.parseList(scanner, parsingIndeces, allowVarRef); err == nil { if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
if parsingIndeces { if parsingIndeces {
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source()) indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
indexTerm := newTerm(indexTk) indexTerm := newTerm(indexTk)
@@ -402,24 +409,24 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
err = currentTerm.Errorf(`selector-case outside of a selector context`) err = currentTerm.Errorf(`selector-case outside of a selector context`)
} else { } else {
var mapTerm *term var mapTerm *term
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil { if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
err = tree.addTerm(mapTerm) err = tree.addTerm(mapTerm)
currentTerm = mapTerm currentTerm = mapTerm
} }
} }
case SymEqual: case SymEqual:
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil { // if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken(tk)
// } // }
case SymFuncDef: case SymFuncDef:
var funcDefTerm *term var funcDefTerm *term
if funcDefTerm, err = self.parseFuncDef(scanner); err == nil { if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
err = tree.addTerm(funcDefTerm) err = tree.addTerm(funcDefTerm)
currentTerm = funcDefTerm currentTerm = funcDefTerm
} }
case SymDollarRound: case SymDollarRound:
var iterDefTerm *term var iterDefTerm *term
if iterDefTerm, err = self.parseIterDef(scanner, allowVarRef); err == nil { if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
err = tree.addTerm(iterDefTerm) err = tree.addTerm(iterDefTerm)
currentTerm = iterDefTerm currentTerm = iterDefTerm
} }
@@ -427,16 +434,16 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
if tk.source[0] == '@' && !allowVarRef { if tk.source[0] == '@' && !allowVarRef {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source) err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
} else { } else {
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken(tk)
} }
case SymQuestion: case SymQuestion:
if selectorTerm, err = self.parseSelector(scanner, tree, allowVarRef); err == nil { if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
currentTerm = selectorTerm currentTerm = selectorTerm
} }
case SymColon, SymDoubleColon: case SymColon, SymDoubleColon:
var caseTerm *term var caseTerm *term
if selectorTerm != nil { if selectorTerm != nil {
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
currentTerm = caseTerm currentTerm = caseTerm
if tk.Sym == SymDoubleColon { if tk.Sym == SymDoubleColon {
@@ -444,14 +451,16 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
} }
} }
} else { } else {
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken(tk)
} }
if tk.IsSymbol(SymColon) { if tk.IsSymbol(SymColon) {
// Colon outside a selector term acts like a separator // Colon outside a selector term acts like a separator
firstToken = true firstToken = true
} }
case SymPlusEqual, SymMinusEqual, SymStarEqual:
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
default: default:
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken(tk)
} }
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector { if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
@@ -460,6 +469,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
} }
// lastSym = tk.Sym // lastSym = tk.Sym
} }
if err == nil { if err == nil {
err = tk.Error() err = tk.Error()
} }
@@ -472,3 +482,49 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
// } // }
// return // return
// } // }
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
var opSym Symbol
var opString string
if tree.root != nil {
switch tk.Sym {
case SymPlusEqual:
opString = "+"
opSym = SymPlus
case SymMinusEqual:
opString = "-"
opSym = SymMinus
case SymStarEqual:
opString = "*"
opSym = SymStar
default:
err = tk.Errorf("unsopported operator %q", tk.source)
return
}
leftExpr := tree.root.Clone()
leftExpr.setParent(nil)
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
t = leftExpr
if err = tree.addTerm(leftExpr); err == nil {
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
var subTree *ast
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
if err = scanner.UnreadToken(); err != nil {
return
}
}
subTree.root.priority = priValue
err = tree.addTerm(newExprTerm(subTree.root))
t = subTree.root
}
}
}
}
} else {
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
}
return
}
+25 -1
View File
@@ -6,6 +6,7 @@ package expr
import ( import (
"fmt" "fmt"
"io"
"os" "os"
"plugin" "plugin"
"strings" "strings"
@@ -29,7 +30,7 @@ func pluginExists(name string) (exists bool) {
func makePluginName(name string) (decorated string) { func makePluginName(name string) (decorated string) {
var template string var template string
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 { if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
template = "expr-%s-plugin.so" template = "expr-%s-plugin.so"
} else { } else {
template = "expr-%s-plugin.so.debug" template = "expr-%s-plugin.so.debug"
@@ -90,6 +91,29 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
return return
} }
func importPluginFromSearchPath(name any) (count int, err error) {
var moduleSpec any
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
count = 0
it := NewAnyIterator(name)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil {
break
}
count++
} else {
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
return
}
func loadModules(dirList []string, moduleNames []string) (err error) { func loadModules(dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames { for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil { if err1 := importPlugin(dirList, name); err1 != nil {
+207 -187
View File
@@ -16,6 +16,7 @@ import (
type scanner struct { type scanner struct {
current *Token current *Token
prev *Token prev *Token
stage *Token
stream *bufio.Reader stream *bufio.Reader
row int row int
column int column int
@@ -49,255 +50,274 @@ func DefaultTranslations() map[Symbol]Symbol {
// return self.current // return self.current
// } // }
func (self *scanner) readChar() (ch byte, err error) { func (scanner *scanner) readChar() (ch byte, err error) {
if ch, err = self.stream.ReadByte(); err == nil { if ch, err = scanner.stream.ReadByte(); err == nil {
if ch == '\n' { if ch == '\n' {
self.row++ scanner.row++
self.column = 0 scanner.column = 0
} else { } else {
self.column++ scanner.column++
} }
} }
return return
} }
func (self *scanner) unreadChar() (err error) { func (scanner *scanner) unreadChar() (err error) {
if err = self.stream.UnreadByte(); err == nil { if err = scanner.stream.UnreadByte(); err == nil {
if self.column--; self.column == 0 { if scanner.column--; scanner.column == 0 {
if self.row--; self.row == 0 { if scanner.row--; scanner.row == 0 {
err = errors.New("unread beyond the stream boundary") err = errors.New("unread beyond the stream boundary")
} else { } else {
self.column = 1 scanner.column = 1
} }
} }
} }
return return
} }
func (self *scanner) lastPos() (r, c int) { func (scanner *scanner) UnreadToken() (err error) {
if self.prev != nil { if scanner.stage == nil {
r = self.prev.row scanner.stage = scanner.current
c = self.prev.col scanner.current = scanner.prev
} else {
err = fmt.Errorf("staging already present, currently one level only of staging is allowed")
} }
return return
} }
func (self *scanner) Previous() *Token { func (scanner *scanner) lastPos() (r, c int) {
return self.prev if scanner.prev != nil {
r = scanner.prev.row
c = scanner.prev.col
}
return
} }
func (self *scanner) Next() (tk *Token) { func (scanner *scanner) Previous() *Token {
self.prev = self.current return scanner.prev
tk = self.current }
self.current = self.fetchNextToken()
func (scanner *scanner) Next() (tk *Token) {
scanner.prev = scanner.current
tk = scanner.current
if scanner.stage != nil {
scanner.current = scanner.stage
scanner.stage = nil
} else {
scanner.current = scanner.fetchNextToken()
}
return tk return tk
} }
func (self *scanner) fetchNextToken() (tk *Token) { func (scanner *scanner) fetchNextToken() (tk *Token) {
var ch byte var ch byte
if err := self.skipBlanks(); err != nil { if err := scanner.skipBlanks(); err != nil {
return self.makeErrorToken(err) return scanner.makeErrorToken(err)
} }
escape := false escape := false
for { for {
ch, _ = self.readChar() ch, _ = scanner.readChar()
switch ch { switch ch {
case '+': case '+':
if next, _ := self.peek(); next == '+' { if next, _ := scanner.peek(); next == '+' {
tk = self.moveOn(SymDoublePlus, ch, next) tk = scanner.moveOn(SymDoublePlus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = self.moveOn(SymPlusEqual, ch, next) tk = scanner.moveOn(SymPlusEqual, ch, next)
} else { } else {
tk = self.makeToken(SymPlus, ch) tk = scanner.makeToken(SymPlus, ch)
} }
case '-': case '-':
if next, _ := self.peek(); next == '-' { if next, _ := scanner.peek(); next == '-' {
tk = self.moveOn(SymDoubleMinus, ch, next) tk = scanner.moveOn(SymDoubleMinus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = self.moveOn(SymMinusEqual, ch, next) tk = scanner.moveOn(SymMinusEqual, ch, next)
} else { } else {
tk = self.makeToken(SymMinus, ch) tk = scanner.makeToken(SymMinus, ch)
} }
case '*': case '*':
if next, _ := self.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
tk = self.moveOn(SymDoubleStar, ch, next) tk = scanner.moveOn(SymDoubleStar, ch, next)
// } else if next == '/' { // } else if next == '/' {
// tk = self.moveOn(SymClosedComment, ch, next) // tk = self.moveOn(SymClosedComment, ch, next)
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymStarEqual, ch, next)
} else { } else {
tk = self.makeToken(SymStar, ch) tk = scanner.makeToken(SymStar, ch)
} }
case '/': case '/':
if next, _ := self.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
self.readChar() scanner.readChar()
tk = self.fetchBlockComment() tk = scanner.fetchBlockComment()
} else if next == '/' { } else if next == '/' {
self.readChar() scanner.readChar()
tk = self.fetchOnLineComment() tk = scanner.fetchOnLineComment()
} else { } else {
tk = self.makeToken(SymSlash, ch) tk = scanner.makeToken(SymSlash, ch)
} }
case '\\': case '\\':
if escape { if escape {
tk = self.makeToken(SymBackSlash, ch) tk = scanner.makeToken(SymBackSlash, ch)
escape = false escape = false
} else { } else {
escape = true escape = true
} }
case '|': case '|':
if next, _ := self.peek(); next == '|' { if next, _ := scanner.peek(); next == '|' {
tk = self.moveOn(SymDoubleVertBar, ch, next) tk = scanner.moveOn(SymDoubleVertBar, ch, next)
} else { } else {
tk = self.makeToken(SymVertBar, ch) tk = scanner.makeToken(SymVertBar, ch)
} }
case ',': case ',':
tk = self.makeToken(SymComma, ch) tk = scanner.makeToken(SymComma, ch)
case '^': case '^':
tk = self.makeToken(SymCaret, ch) tk = scanner.makeToken(SymCaret, ch)
case ':': case ':':
if next, _ := self.peek(); next == ':' { if next, _ := scanner.peek(); next == ':' {
tk = self.moveOn(SymDoubleColon, ch, next) tk = scanner.moveOn(SymDoubleColon, ch, next)
} else { } else {
tk = self.makeToken(SymColon, ch) tk = scanner.makeToken(SymColon, ch)
} }
case ';': case ';':
tk = self.makeToken(SymSemiColon, ch) tk = scanner.makeToken(SymSemiColon, ch)
case '.': case '.':
//if next, _ := self.peek(); next >= '0' && next <= '9' { //if next, _ := self.peek(); next >= '0' && next <= '9' {
// tk = self.parseNumber(ch) // tk = self.parseNumber(ch)
//} else if next == '/' { //} else if next == '/' {
if next, _ := self.peek(); next == '/' { if next, _ := scanner.peek(); next == '/' {
tk = self.moveOn(SymDotSlash, ch, next) tk = scanner.moveOn(SymDotSlash, ch, next)
} else if next == '.' { } else if next == '.' {
if next1, _ := self.peek(); next1 == '.' { if next1, _ := scanner.peek(); next1 == '.' {
tk = self.moveOn(SymTripleDot, ch, next, next1) tk = scanner.moveOn(SymTripleDot, ch, next, next1)
} else { } else {
tk = self.moveOn(SymDoubleDot, ch, next) tk = scanner.moveOn(SymDoubleDot, ch, next)
} }
} else { } else {
tk = self.makeToken(SymDot, ch) tk = scanner.makeToken(SymDot, ch)
} }
case '\'': case '\'':
if escape { if escape {
tk = self.makeToken(SymQuote, ch) tk = scanner.makeToken(SymQuote, ch)
escape = false escape = false
} else { } else {
tk = self.fetchString(ch) tk = scanner.fetchString(ch)
} }
case '"': case '"':
if escape { if escape {
tk = self.makeToken(SymDoubleQuote, ch) tk = scanner.makeToken(SymDoubleQuote, ch)
escape = false escape = false
} else { } else {
tk = self.fetchString(ch) tk = scanner.fetchString(ch)
} }
case '`': case '`':
tk = self.makeToken(SymBackTick, ch) tk = scanner.makeToken(SymBackTick, ch)
case '!': case '!':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymNotEqual, ch, next) tk = scanner.moveOn(SymNotEqual, ch, next)
} else { } else {
tk = self.makeToken(SymExclamation, ch) tk = scanner.makeToken(SymExclamation, ch)
} }
case '?': case '?':
if next, _ := self.peek(); next == '?' { if next, _ := scanner.peek(); next == '?' {
tk = self.moveOn(SymDoubleQuestion, ch, next) tk = scanner.moveOn(SymDoubleQuestion, ch, next)
} else if next, _ := self.peek(); next == '=' { } else if next == '=' {
tk = self.moveOn(SymQuestionEqual, ch, next) tk = scanner.moveOn(SymQuestionEqual, ch, next)
} else if next == '!' {
tk = scanner.moveOn(SymQuestionExclam, ch, next)
} else { } else {
tk = self.makeToken(SymQuestion, ch) tk = scanner.makeToken(SymQuestion, ch)
} }
case '&': case '&':
if next, _ := self.peek(); next == '&' { if next, _ := scanner.peek(); next == '&' {
tk = self.moveOn(SymDoubleAmpersand, ch, next) tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
} else { } else {
tk = self.makeToken(SymAmpersand, ch) tk = scanner.makeToken(SymAmpersand, ch)
} }
case '%': case '%':
tk = self.makeToken(SymPercent, ch) tk = scanner.makeToken(SymPercent, ch)
case '#': case '#':
tk = self.makeToken(SymHash, ch) tk = scanner.makeToken(SymHash, ch)
case '@': case '@':
if next, _ := self.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') { if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
self.readChar() scanner.readChar()
if tk = self.fetchIdentifier(next); tk.Sym == SymIdentifier { if tk = scanner.fetchIdentifier(next); tk.Sym == SymIdentifier {
//tk.Sym = SymIdRef //tk.Sym = SymIdRef
tk.source = "@" + tk.source tk.source = "@" + tk.source
} else { } else {
tk = self.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source)) tk = scanner.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source))
} }
} else if next == '@' { } else if next == '@' {
tk = self.moveOn(SymDoubleAt, ch, next) tk = scanner.moveOn(SymDoubleAt, ch, next)
} else { } else {
tk = self.makeToken(SymAt, ch) tk = scanner.makeToken(SymAt, ch)
} }
case '_': case '_':
tk = self.makeToken(SymUndescore, ch) tk = scanner.makeToken(SymUndescore, ch)
case '=': case '=':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymDoubleEqual, ch, next) tk = scanner.moveOn(SymDoubleEqual, ch, next)
} else { } else {
tk = self.makeToken(SymEqual, ch) tk = scanner.makeToken(SymEqual, ch)
} }
case '<': case '<':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymLessOrEqual, ch, next) tk = scanner.moveOn(SymLessOrEqual, ch, next)
} else if next == '<' { } else if next == '<' {
tk = self.moveOn(SymAppend, ch, next) tk = scanner.moveOn(SymAppend, ch, next)
} else if next == '>' { } else if next == '>' {
tk = self.moveOn(SymLessGreater, ch, next) tk = scanner.moveOn(SymLessGreater, ch, next)
} else { } else {
tk = self.makeToken(SymLess, ch) tk = scanner.makeToken(SymLess, ch)
} }
case '>': case '>':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymGreaterOrEqual, ch, next) tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
} else if next == '>' { } else if next == '>' {
tk = self.moveOn(SymInsert, ch, next) tk = scanner.moveOn(SymInsert, ch, next)
} else { } else {
tk = self.makeToken(SymGreater, ch) tk = scanner.makeToken(SymGreater, ch)
} }
case '$': case '$':
if next, _ := self.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = self.moveOn(SymDollarRound, ch, next) tk = scanner.moveOn(SymDollarRound, ch, next)
tk.source += ")" tk.source += ")"
} else if next == '$' { } else if next == '$' {
tk = self.moveOn(SymDoubleDollar, ch, next) tk = scanner.moveOn(SymDoubleDollar, ch, next)
} else { } else {
tk = self.makeToken(SymDollar, ch) tk = scanner.makeToken(SymDollar, ch)
} }
case '(': case '(':
if next, _ := self.peek(); next == ')' { // if next, _ := scanner.peek(); next == ')' {
tk = self.moveOn(SymOpenClosedRound, ch, next) // tk = scanner.moveOn(SymOpenClosedRound, ch, next)
} else { // } else {
tk = self.makeToken(SymOpenRound, ch) tk = scanner.makeToken(SymOpenRound, ch)
} // }
case ')': case ')':
tk = self.makeToken(SymClosedRound, ch) tk = scanner.makeToken(SymClosedRound, ch)
case '[': case '[':
tk = self.makeToken(SymOpenSquare, ch) tk = scanner.makeToken(SymOpenSquare, ch)
case ']': case ']':
tk = self.makeToken(SymClosedSquare, ch) tk = scanner.makeToken(SymClosedSquare, ch)
case '{': case '{':
tk = self.makeToken(SymOpenBrace, ch) tk = scanner.makeToken(SymOpenBrace, ch)
case '}': case '}':
tk = self.makeToken(SymClosedBrace, ch) tk = scanner.makeToken(SymClosedBrace, ch)
case '~': case '~':
tk = self.makeToken(SymTilde, ch) tk = scanner.makeToken(SymTilde, ch)
case 0: case 0:
if escape { if escape {
tk = self.makeErrorToken(errors.New("incomplete escape sequence")) tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
} }
escape = false escape = false
default: default:
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
if tk = self.fetchIdentifier(ch); tk.Sym == SymKwFunc { if tk = scanner.fetchIdentifier(ch); tk.Sym == SymKwFunc {
if next, _ := self.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = self.moveOn(SymFuncDef, ch, next) tk = scanner.moveOn(SymFuncDef, ch, next)
} }
} }
} else if ch >= '0' && ch <= '9' { } else if ch >= '0' && ch <= '9' {
tk = self.parseNumber(ch) tk = scanner.parseNumber(ch)
} }
} }
if !escape { if !escape {
@@ -305,14 +325,14 @@ func (self *scanner) fetchNextToken() (tk *Token) {
} }
} }
if tk == nil { if tk == nil {
tk = NewErrorToken(self.row, self.column, fmt.Errorf("unknown symbol '%c'", ch)) tk = NewErrorToken(scanner.row, scanner.column, fmt.Errorf("unknown symbol '%c'", ch))
} }
return return
} }
func (self *scanner) sync(err error) error { func (scanner *scanner) sync(err error) error {
if err == nil { if err == nil {
err = self.unreadChar() err = scanner.unreadChar()
} }
return err return err
} }
@@ -333,32 +353,32 @@ func isHexDigit(ch byte) bool {
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
} }
func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) { func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) {
var ch byte var ch byte
var digitType string var digitType string
firstCh = currentFirstCh firstCh = currentFirstCh
digitFunc = isDecimalDigit digitFunc = isDecimalDigit
numBase = 10 numBase = 10
if ch, err = self.peek(); err == nil { if ch, err = scanner.peek(); err == nil {
if ch == 'b' || ch == 'B' { if ch == 'b' || ch == 'B' {
numBase = 2 numBase = 2
digitType = "binary" digitType = "binary"
self.readChar() scanner.readChar()
digitFunc = isBinaryDigit digitFunc = isBinaryDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} else if ch == 'o' || ch == 'O' { } else if ch == 'o' || ch == 'O' {
numBase = 8 numBase = 8
digitType = "octal" digitType = "octal"
self.readChar() scanner.readChar()
digitFunc = isOctalDigit digitFunc = isOctalDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} else if ch == 'x' || ch == 'X' { } else if ch == 'x' || ch == 'X' {
numBase = 16 numBase = 16
digitType = "hex" digitType = "hex"
self.readChar() scanner.readChar()
digitFunc = isHexDigit digitFunc = isHexDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} }
if err == nil && !digitFunc(firstCh) { if err == nil && !digitFunc(firstCh) {
if len(digitType) == 0 { if len(digitType) == 0 {
@@ -372,7 +392,7 @@ func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh
return return
} }
func (self *scanner) parseNumber(firstCh byte) (tk *Token) { func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sym Symbol = SymInteger var sym Symbol = SymInteger
@@ -381,9 +401,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
var numBase = 10 var numBase = 10
if firstCh == '0' { if firstCh == '0' {
firstCh, numBase, isDigit, err = self.initBase(&sb, firstCh) firstCh, numBase, isDigit, err = scanner.initBase(firstCh)
} }
for ch = firstCh; err == nil && isDigit(ch); ch, err = self.readChar() { for ch = firstCh; err == nil && isDigit(ch); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
@@ -391,9 +411,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
if err == nil && ch == '.' { if err == nil && ch == '.' {
sym = SymFloat sym = SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
if ch >= '0' && ch <= '9' { if ch >= '0' && ch <= '9' {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
} }
@@ -402,32 +422,32 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
if ch == 'e' || ch == 'E' { if ch == 'e' || ch == 'E' {
sym = SymFloat sym = SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
if ch, err = self.readChar(); err == nil { if ch, err = scanner.readChar(); err == nil {
if ch == '+' || ch == '-' { if ch == '+' || ch == '-' {
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
} }
if ch >= '0' && ch <= '9' { if ch >= '0' && ch <= '9' {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
} else { } else {
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", self.row, self.column, ch) err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", scanner.row, scanner.column, ch)
} }
} }
} else if ch == '(' { } else if ch == '(' {
sym = SymFraction sym = SymFraction
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
if err == nil { if err == nil {
if ch != ')' { if ch != ')' {
err = fmt.Errorf("[%d:%d] expected ')', got '%c'", self.row, self.column, ch) err = fmt.Errorf("[%d:%d] expected ')', got '%c'", scanner.row, scanner.column, ch)
} else { } else {
sb.WriteByte(ch) sb.WriteByte(ch)
_, err = self.readChar() _, err = scanner.readChar()
} }
} }
} }
@@ -435,10 +455,10 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else { } else {
var value any var value any
err = self.sync(err) err = scanner.sync(err) // TODO: Check this function
txt := sb.String() txt := sb.String()
if sym == SymFloat { if sym == SymFloat {
value, err = strconv.ParseFloat(txt, 64) value, err = strconv.ParseFloat(txt, 64)
@@ -448,39 +468,39 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
value, err = strconv.ParseInt(txt, numBase, 64) value, err = strconv.ParseInt(txt, numBase, 64)
} }
if err == nil { if err == nil {
tk = self.makeValueToken(sym, txt, value) tk = scanner.makeValueToken(sym, txt, value)
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
} }
return return
} }
func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) { func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
var err error var err error
var sb strings.Builder var sb strings.Builder
for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = self.readChar() { for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else if err = self.sync(err); err != nil && err != io.EOF { } else if err = scanner.sync(err); err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else { } else {
txt := sb.String() txt := sb.String()
uptxt := strings.ToUpper(txt) uptxt := strings.ToUpper(txt)
if sym, ok := keywords[uptxt]; ok { if sym, ok := keywords[uptxt]; ok {
tk = self.makeKeywordToken(sym, uptxt) tk = scanner.makeKeywordToken(sym, uptxt)
} else if uptxt == `TRUE` { } else if uptxt == `TRUE` {
tk = self.makeValueToken(SymBool, txt, true) tk = scanner.makeValueToken(SymBool, txt, true)
} else if uptxt == `FALSE` { } else if uptxt == `FALSE` {
tk = self.makeValueToken(SymBool, txt, false) tk = scanner.makeValueToken(SymBool, txt, false)
} else if ch, _ := self.peek(); ch == '(' { } else if ch, _ := scanner.peek(); ch == '(' {
self.readChar() scanner.readChar()
tk = self.makeValueToken(SymFuncCall, txt+"(", txt) tk = scanner.makeValueToken(SymFuncCall, txt+"(", txt)
} else { } else {
tk = self.makeValueToken(SymIdentifier, txt, txt) tk = scanner.makeValueToken(SymIdentifier, txt, txt)
} }
} }
@@ -500,29 +520,29 @@ func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
return return
} }
func (self *scanner) fetchBlockComment() *Token { func (scanner *scanner) fetchBlockComment() *Token {
return self.fetchUntil(SymComment, false, '*', '/') return scanner.fetchUntil(SymComment, false, '*', '/')
} }
func (self *scanner) fetchOnLineComment() *Token { func (scanner *scanner) fetchOnLineComment() *Token {
return self.fetchUntil(SymComment, true, '\n') return scanner.fetchUntil(SymComment, true, '\n')
} }
func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) { func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sb strings.Builder var sb strings.Builder
var value string var value string
ring := NewByteSlider(len(endings)) ring := NewByteSlider(len(endings))
endReached := false endReached := false
for ch, err = self.readChar(); err == nil && !endReached; { for ch, err = scanner.readChar(); err == nil && !endReached; {
sb.WriteByte(ch) sb.WriteByte(ch)
ring.PushEnd(ch) ring.PushEnd(ch)
if ring.Equal(endings) { if ring.Equal(endings) {
value = sb.String()[0 : sb.Len()-len(endings)] value = sb.String()[0 : sb.Len()-len(endings)]
endReached = true endReached = true
} else { } else {
ch, err = self.readChar() ch, err = scanner.readChar()
} }
} }
if !endReached && allowEos { if !endReached && allowEos {
@@ -531,18 +551,18 @@ func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk
} }
if endReached { if endReached {
tk = self.makeValueToken(sym, "", value) tk = scanner.makeValueToken(sym, "", value)
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
return return
} }
func (self *scanner) fetchString(termCh byte) (tk *Token) { func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
var err error var err error
var ch, prev byte var ch, prev byte
var sb strings.Builder var sb strings.Builder
for ch, err = self.readChar(); err == nil; ch, err = self.readChar() { for ch, err = scanner.readChar(); err == nil; ch, err = scanner.readChar() {
if prev == '\\' { if prev == '\\' {
switch ch { switch ch {
case '"': case '"':
@@ -570,65 +590,65 @@ func (self *scanner) fetchString(termCh byte) (tk *Token) {
} }
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
tk = self.makeErrorToken(errors.New("missing string termination \"")) tk = scanner.makeErrorToken(errors.New("missing string termination \""))
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
} else { } else {
txt := sb.String() txt := sb.String()
tk = self.makeValueToken(SymString, `"`+txt+`"`, txt) tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
} }
return return
} }
func (self *scanner) peek() (next byte, err error) { func (scanner *scanner) peek() (next byte, err error) {
var one []byte var one []byte
if one, err = self.stream.Peek(1); err == nil { if one, err = scanner.stream.Peek(1); err == nil {
next = one[0] next = one[0]
} }
return return
} }
func (self *scanner) skipBlanks() (err error) { func (scanner *scanner) skipBlanks() (err error) {
var one []byte var one []byte
for one, err = self.stream.Peek(1); err == nil && one[0] <= 32; one, err = self.stream.Peek(1) { for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) {
self.readChar() scanner.readChar()
} }
return return
} }
func (self *scanner) translate(sym Symbol) Symbol { func (scanner *scanner) translate(sym Symbol) Symbol {
if self.translations != nil { if scanner.translations != nil {
if translatedSym, ok := self.translations[sym]; ok { if translatedSym, ok := scanner.translations[sym]; ok {
return translatedSym return translatedSym
} }
} }
return sym return sym
} }
func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
for i := 1; i < len(chars); i++ { for i := 1; i < len(chars); i++ {
self.readChar() scanner.readChar()
} }
return return
} }
func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) { func (scanner *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) { func (scanner *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), upperCaseKeyword) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
return return
} }
func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) { func (scanner *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
tk = NewValueToken(self.row, self.column, self.translate(sym), source, value) tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
return return
} }
func (self *scanner) makeErrorToken(err error) *Token { func (scanner *scanner) makeErrorToken(err error) *Token {
return NewErrorToken(self.row, self.column, err) return NewErrorToken(scanner.row, scanner.column, err)
} }
+21 -4
View File
@@ -25,7 +25,7 @@ func NewSimpleStore() *SimpleStore {
} }
func filterRefName(name string) bool { return name[0] != '@' } func filterRefName(name string) bool { return name[0] != '@' }
func filterPrivName(name string) bool { return name[0] != '_' } //func filterPrivName(name string) bool { return name[0] != '_' }
func (ctx *SimpleStore) SetParent(parentCtx ExprContext) { func (ctx *SimpleStore) SetParent(parentCtx ExprContext) {
ctx.parent = parentCtx ctx.parent = parentCtx
@@ -132,6 +132,14 @@ func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varN
return return
} }
func (ctx *SimpleStore) VarCount() int {
return len(ctx.varStore)
}
func (ctx *SimpleStore) DeleteVar(varName string) {
delete(ctx.varStore, varName)
}
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) { func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
info, exists = ctx.funcStore[name] info, exists = ctx.funcStore[name]
return return
@@ -141,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
} }
@@ -163,10 +172,18 @@ func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (fun
return return
} }
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) { func (ctx *SimpleStore) FuncCount() int {
return len(ctx.funcStore)
}
func (ctx *SimpleStore) DeleteFunc(funcName string) {
delete(ctx.funcStore, funcName)
}
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)
} }
+14 -10
View File
@@ -57,16 +57,18 @@ const (
SymTilde // 46: '~' SymTilde // 46: '~'
SymDoubleQuestion // 47: '??' SymDoubleQuestion // 47: '??'
SymQuestionEqual // 48: '?=' SymQuestionEqual // 48: '?='
SymDoubleAt // 49: '@@' SymQuestionExclam // 49: '?!'
SymDoubleColon // 50: '::' SymDoubleAt // 50: '@@'
SymInsert // 51: '>>' SymDoubleColon // 51: '::'
SymAppend // 52: '<<' SymInsert // 52: '>>'
SymCaret // 53: '^' SymAppend // 53: '<<'
SymDollarRound // 54: '$(' SymCaret // 54: '^'
SymOpenClosedRound // 55: '()' SymDollarRound // 55: '$('
SymDoubleDollar // 56: '$$' SymOpenClosedRound // 56: '()'
SymDoubleDot // 57: '..' SymDoubleDollar // 57: '$$'
SymTripleDot // 58: '...' SymDoubleDot // 58: '..'
SymTripleDot // 59: '...'
SymStarEqual // 60: '*='
SymChangeSign SymChangeSign
SymUnchangeSign SymUnchangeSign
SymIdentifier SymIdentifier
@@ -105,6 +107,7 @@ const (
SymKwIn SymKwIn
SymKwInclude SymKwInclude
SymKwNil SymKwNil
SymKwUnset
) )
var keywords map[string]Symbol var keywords map[string]Symbol
@@ -122,5 +125,6 @@ func init() {
"NOT": SymKwNot, "NOT": SymKwNot,
"OR": SymKwOr, "OR": SymKwOr,
"NIL": SymKwNil, "NIL": SymKwNil,
"UNSET": SymKwUnset,
} }
} }
+1 -1
View File
@@ -47,7 +47,7 @@ func TestAddUnknownTokens(t *testing.T) {
wantErr := errors.New(`unexpected token "%"`) wantErr := errors.New(`unexpected token "%"`)
tree := NewAst() tree := NewAst()
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { if _, gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr) t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr)
} }
} }
+5 -5
View File
@@ -45,13 +45,13 @@ func TestBoolNoShortcut(t *testing.T) {
/* 5 */ {`not "true"`, false, nil}, /* 5 */ {`not "true"`, false, nil},
/* 6 */ {`not "false"`, false, nil}, /* 6 */ {`not "false"`, false, nil},
/* 7 */ {`not ""`, true, nil}, /* 7 */ {`not ""`, true, nil},
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`)}, /* 8 */ {`not []`, nil, `[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`},
/* 9 */ {`true and false`, false, nil}, /* 9 */ {`true and false`, false, nil},
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)}, /* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`},
/* 11 */ {`[] and false`, nil, errors.New(`[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`)}, /* 11 */ {`[] and false`, nil, `[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`},
/* 12 */ {`true or false`, true, nil}, /* 12 */ {`true or false`, true, nil},
/* 13 */ {`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)}, /* 13 */ {`true or []`, nil, `[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`},
/* 14 */ {`[] or false`, nil, errors.New(`[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`)}, /* 14 */ {`[] or false`, nil, `[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
+10 -11
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -21,9 +20,9 @@ func TestFuncBase(t *testing.T) {
/* 6 */ {`int(3.1)`, int64(3), nil}, /* 6 */ {`int(3.1)`, int64(3), nil},
/* 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, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, /* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
/* 10 */ {`int("432", 4)`, nil, errors.New(`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, errors.New(`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},
/* 14 */ {`isFloat(3.1)`, true, nil}, /* 14 */ {`isFloat(3.1)`, true, nil},
@@ -42,9 +41,9 @@ func TestFuncBase(t *testing.T) {
/* 27 */ {`dec(2.0)`, float64(2), nil}, /* 27 */ {`dec(2.0)`, float64(2), nil},
/* 28 */ {`dec("2.0")`, float64(2), nil}, /* 28 */ {`dec("2.0")`, float64(2), nil},
/* 29 */ {`dec(true)`, float64(1), nil}, /* 29 */ {`dec(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")}, /* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
/* 31 */ {`dec()`, nil, errors.New(`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, errors.New(`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},
@@ -53,15 +52,15 @@ func TestFuncBase(t *testing.T) {
/* 38 */ {`bool(1.0)`, true, nil}, /* 38 */ {`bool(1.0)`, true, nil},
/* 39 */ {`bool("1")`, true, nil}, /* 39 */ {`bool("1")`, true, nil},
/* 40 */ {`bool(false)`, false, nil}, /* 40 */ {`bool(false)`, false, nil},
/* 41 */ {`bool([1])`, nil, errors.New(`bool(): can't convert list to bool`)}, /* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
/* 42 */ {`dec(false)`, float64(0), nil}, /* 42 */ {`dec(false)`, float64(0), nil},
/* 43 */ {`dec(1|2)`, float64(0.5), nil}, /* 43 */ {`dec(1|2)`, float64(0.5), nil},
/* 44 */ {`dec([1])`, nil, errors.New(`dec(): can't convert list to float`)}, /* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
// /* 45 */ {`string([1])`, nil, errors.New(`string(): can't convert list to string`)}, // /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 2) //runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+2 -2
View File
@@ -21,7 +21,7 @@ func TestFuncFmt(t *testing.T) {
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 19) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
@@ -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", ".")
+1 -1
View File
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
t.Setenv("EXPR_PATH", "test-resources") t.Setenv("EXPR_PATH", "test-resources")
// parserTestSpec(t, section, inputs, 69) //runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_builtin-iterator.go
package expr
import (
"testing"
)
func TestFuncRun(t *testing.T) {
section := "Builtin-Iterator"
inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil},
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
}
//t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}
+7 -3
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -14,16 +13,21 @@ func TestFuncMathArith(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil}, /* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil}, /* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)}, /* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, `unknown function mulX()`},
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil}, /* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil}, /* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
/* 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", ".")
// parserTestSpec(t, section, inputs, 69) //runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+10 -11
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -14,23 +13,23 @@ func TestFuncOs(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "os.file"`, int64(1), nil}, /* 1 */ {`builtin "os.file"`, int64(1), nil},
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil}, /* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)}, /* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, `open /etc/hostsX: no such file or directory`},
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil}, /* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil}, /* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil}, /* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)}, /* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, `fileReadText(): invalid file handle`},
/* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)}, /* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, `fileWriteText(): invalid file handle`},
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, errors.New(`fileOpen(): too few params -- expected 1, got 0`)}, /* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, `fileOpen(): too few params -- expected 1, got 0`},
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, errors.New(`fileOpen(): missing or invalid file path`)}, /* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, `fileOpen(): missing or invalid file path`},
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, errors.New(`fileCreate(): missing or invalid file path`)}, /* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, `fileCreate(): missing or invalid file path`},
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, errors.New(`fileAppend(): missing or invalid file path`)}, /* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, `fileAppend(): missing or invalid file path`},
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, errors.New(`fileClose(): invalid file handle`)}, /* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil}, /* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, errors.New(`fileReadTextAll(): invalid file handle 123 [int64]`)}, /* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69) //runTestSuiteSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+6 -7
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -16,8 +15,8 @@ func TestFuncString(t *testing.T) {
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil}, /* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil}, /* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil}, /* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)}, /* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)}, /* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil}, /* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil}, /* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil}, /* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
@@ -25,13 +24,13 @@ func TestFuncString(t *testing.T) {
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil}, /* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil}, /* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil}, /* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)}, /* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, `strStartsWith(): too few params -- expected 2 or more, got 1`},
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil}, /* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil}, /* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)}, /* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`},
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil}, /* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)}, /* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)}, /* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
/* 69 */ /*{`builtin "string"; $$global`, `vars: { /* 69 */ /*{`builtin "string"; $$global`, `vars: {
} }
+21 -19
View File
@@ -5,6 +5,7 @@
package expr package expr
import ( import (
"errors"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@@ -13,7 +14,7 @@ import (
type inputType struct { type inputType struct {
source string source string
wantResult any wantResult any
wantErr error wantErr any
} }
func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) { func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
@@ -41,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++
@@ -52,20 +55,17 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
} }
func runTestSuite(t *testing.T, section string, inputs []inputType) { func runTestSuite(t *testing.T, section string, inputs []inputType) {
runCtxTestSuite(t, nil, section, inputs) runCtxTestSuite(t, nil, section, inputs)
/* }
succeeded := 0
failed := 0
for i, input := range inputs { func getWantedError(input *inputType) error {
good := doTest(t, nil, section, &input, i+1) var wantErr error
if good { var ok bool
succeeded++ if wantErr, ok = input.wantErr.(error); !ok {
} else { if msg, ok := input.wantErr.(string); ok {
failed++ wantErr = errors.New(msg)
} }
} }
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) return wantErr
*/
} }
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) { func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
@@ -73,12 +73,14 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
var gotResult any var gotResult any
var gotErr error var gotErr error
wantErr := getWantedError(input)
parser := NewParser() parser := NewParser()
if ctx == nil { if ctx == nil {
ctx = NewSimpleStore() ctx = NewSimpleStore()
} }
logTest(t, count, section, input.source, input.wantResult, input.wantErr) logTest(t, count, section, input.source, input.wantResult, wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@@ -91,13 +93,13 @@ 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
} }
if gotErr != input.wantErr { if gotErr != wantErr {
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr) t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
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)
} }
} }
+7 -2
View File
@@ -22,7 +22,7 @@ func TestDictParser(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`{}`, map[any]any{}, nil}, /* 1 */ {`{}`, map[any]any{}, nil},
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)}, /* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil}, /* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil}, /* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil}, /* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
@@ -32,7 +32,12 @@ func TestDictParser(t *testing.T) {
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil}, /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)}, /* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil}, /* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
} /* 12 */ {`m={
"a":1,
//"b":2,
"c":3
}`, map[any]any{"a": 1, "c": 3}, nil},
}
succeeded := 0 succeeded := 0
failed := 0 failed := 0
+15 -4
View File
@@ -17,10 +17,21 @@ func TestExpr(t *testing.T) {
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil}, /* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {` /* 6 */ {`a=3; a+=1`, int64(4), nil},
/* 7 */ {`a=3; a-=1`, int64(2), nil},
/* 8 */ {`a=3; a*=2`, int64(6), nil},
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
/* 17 */ {`
ds={ ds={
"init":func(end){@end=end; @current=0 but true}, "init":func(@end){@current=0 but true},
"current":func(){current}, //"current":func(){current},
"next":func(){ "next":func(){
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil} ((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
} }
@@ -32,6 +43,6 @@ func TestExpr(t *testing.T) {
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 3) // runTestSuiteSpec(t, section, inputs, 17)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+8 -8
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -18,21 +17,22 @@ func TestFractionsParser(t *testing.T) {
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil}, /* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil}, /* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil}, /* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
/* 7 */ {`1|"5"`, nil, errors.New(`denominator must be integer, got string (5)`)}, /* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
/* 8 */ {`"1"|5`, nil, errors.New(`numerator must be integer, got string (1)`)}, /* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
/* 9 */ {`1|+5`, nil, errors.New(`[1:3] infix operator "|" requires two non-nil operands, got 1`)}, /* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil}, /* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil}, /* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil}, /* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil}, /* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, /* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
/* 15 */ {`1|0`, nil, errors.New(`division by zero`)}, /* 15 */ {`1|0`, nil, `division by zero`},
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil}, /* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
/* 17 */ {`fract("")`, (*FractionType)(nil), errors.New(`bad syntax`)}, /* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil}, /* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil}, /* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)}, /* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)}, /* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
/* 22 */ {`string(1|2)`, "1|2", nil},
} }
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+14 -8
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -17,30 +16,37 @@ func TestFuncs(t *testing.T) {
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil}, /* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil}, /* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil}, /* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
/* 6 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)}, /* 6 */ {`@x="hello"; @x`, nil, `[1:3] variable references are not allowed in top level expressions: "@x"`},
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil}, /* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, /* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)}, /* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, `undefined variable or function "x"`},
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @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}, /* 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, errors.New(`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, errors.New(`[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, errors.New(`[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 */ {`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 ")"`)},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 17) //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
} }
+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`
+1 -2
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -17,7 +16,7 @@ func TestCollections(t *testing.T) {
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil}, /* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
/* 4 */ {`"abcdef"[:]`, "abcdef", nil}, /* 4 */ {`"abcdef"[:]`, "abcdef", nil},
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil}, // /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
/* 5 */ {`"abcdef"[1:2:3]`, nil, errors.New(`[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`)}, /* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
+7 -7
View File
@@ -15,7 +15,7 @@ func TestNewListIterator(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "b" { } else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item) t.Errorf("expected %q, got %q", "b", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -27,7 +27,7 @@ func TestNewListIterator2(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "d" { } else if item != "d" {
t.Errorf("expcted %q, got %q", "d", item) t.Errorf("expected %q, got %q", "d", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -39,7 +39,7 @@ func TestNewListIterator3(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "b" { } else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item) t.Errorf("expected %q, got %q", "b", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -51,7 +51,7 @@ func TestNewIterList2(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -63,7 +63,7 @@ func TestNewIterList3(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -83,7 +83,7 @@ func TestNewIterList5(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "123" { } else if item != "123" {
t.Errorf("expcted %q, got %q", "123", item) t.Errorf("expected %q, got %q", "123", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
@@ -96,7 +96,7 @@ func TestNewIterList6(t *testing.T) {
if item, err := it.Next(); err != nil { if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err) t.Errorf("error: %v", err)
} else if item != "a" { } else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item) t.Errorf("expected %q, got %q", "a", item)
} else { } else {
t.Logf("Next: %v", item) t.Logf("Next: %v", item)
} }
+15 -9
View File
@@ -7,20 +7,26 @@ package expr
import "testing" import "testing"
func TestIteratorParser(t *testing.T) { func TestIteratorParser(t *testing.T) {
section := "Iterator"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, /* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, /* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, /* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, /* 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},
/* 10 */ {`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`, true, nil},
/* 11 */ {`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},
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
/* 13 */ {`it=$([1,2,3,4],1,3); it++; it++;`, int64(3), 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`},
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
} }
// inputs1 := []inputType{
// /* 1 */ {`0?{}`, nil, nil}, // runTestSuiteSpec(t, section, inputs, 11)
// } runTestSuite(t, section, inputs)
runTestSuite(t, "Iterator", inputs)
} }
+11 -12
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -22,14 +21,14 @@ func TestListParser(t *testing.T) {
/* 7 */ {`add([1,4,3,2])`, int64(10), nil}, /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)}, /* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil}, /* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil}, /* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
/* 14 */ {`[1,2,3][1]`, int64(2), nil}, /* 14 */ {`[1,2,3][1]`, int64(2), nil},
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil}, /* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil}, /* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, errors.New(`[1:34] index 10 out of bounds`)}, /* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`},
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil}, /* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil}, /* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil}, /* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
@@ -37,18 +36,18 @@ func TestListParser(t *testing.T) {
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil}, /* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil}, /* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
/* 24 */ {`["a","b","c","d"][1]`, "b", nil}, /* 24 */ {`["a","b","c","d"][1]`, "b", nil},
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)}, /* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, /* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
/* 27 */ {`["a", "b", "c"] << ;`, nil, errors.New(`[1:18] infix operator "<<" requires two non-nil operands, got 1`)}, /* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
/* 28 */ {`2 << 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`)}, /* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
/* 29 */ {`but >> ["a", "b", "c"]`, nil, errors.New(`[1:6] infix operator ">>" requires two non-nil operands, got 0`)}, /* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
/* 30 */ {`2 >> 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`)}, /* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil}, /* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil}, /* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil}, /* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, errors.New(`index 5 out of bounds (0, 1)`)}, /* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, errors.New(`[1:12] index/key specification expected, got [] [list]`)}, /* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, errors.New(`[1:12] index/key is nil`)}, /* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil}, /* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil}, /* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil}, /* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
@@ -57,6 +56,6 @@ func TestListParser(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 17) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_operator_test.go
package expr
import (
"testing"
)
func TestOperator(t *testing.T) {
section := "Operator"
inputs := []inputType{
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
/* 3 */ {`f=func(){3}; unset "f()"`, int64(1), nil},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs)
}
+36 -31
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -49,13 +48,13 @@ func TestGeneralParser(t *testing.T) {
/* 34 */ {`(((1)))`, int64(1), nil}, /* 34 */ {`(((1)))`, int64(1), nil},
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil}, /* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
/* 36 */ {`0 || 0.0 && "hello"`, false, nil}, /* 36 */ {`0 || 0.0 && "hello"`, false, nil},
/* 37 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, /* 37 */ {`"s" + true`, nil, `[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`},
/* 38 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)}, /* 38 */ {`+false`, nil, `[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`},
/* 39 */ {`false // very simple expression`, false, nil}, /* 39 */ {`false // very simple expression`, false, nil},
/* 40 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)}, /* 40 */ {`1 + // Missing right operator`, nil, `[1:4] infix operator "+" requires two non-nil operands, got 1`},
/* 41 */ {"", nil, nil}, /* 41 */ {"", nil, nil},
/* 42 */ {"4!", int64(24), nil}, /* 42 */ {"4!", int64(24), nil},
/* 43 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)}, /* 43 */ {"(-4)!", nil, `factorial of a negative integer (-4) is not allowed`},
/* 44 */ {"-4!", int64(-24), nil}, /* 44 */ {"-4!", int64(-24), nil},
/* 45 */ {"1.5 < 7", true, nil}, /* 45 */ {"1.5 < 7", true, nil},
/* 46 */ {"1.5 > 7", false, nil}, /* 46 */ {"1.5 > 7", false, nil},
@@ -67,20 +66,20 @@ func TestGeneralParser(t *testing.T) {
/* 52 */ {`"1.5" > "7"`, false, nil}, /* 52 */ {`"1.5" > "7"`, false, nil},
/* 53 */ {`"1.5" == "7"`, false, nil}, /* 53 */ {`"1.5" == "7"`, false, nil},
/* 54 */ {`"1.5" != "7"`, true, nil}, /* 54 */ {`"1.5" != "7"`, true, nil},
/* 55 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)}, /* 55 */ {"1.5 < ", nil, `[1:6] infix operator "<" requires two non-nil operands, got 1`},
/* 56 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)}, /* 56 */ {"1.5 > ", nil, `[1:6] infix operator ">" requires two non-nil operands, got 1`},
/* 57 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)}, /* 57 */ {"1.5 <= ", nil, `[1:6] infix operator "<=" requires two non-nil operands, got 1`},
/* 58 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)}, /* 58 */ {"1.5 >= ", nil, `[1:6] infix operator ">=" requires two non-nil operands, got 1`},
/* 59 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)}, /* 59 */ {"1.5 != ", nil, `[1:6] infix operator "!=" requires two non-nil operands, got 1`},
/* 60 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)}, /* 60 */ {"1.5 == ", nil, `[1:6] infix operator "==" requires two non-nil operands, got 1`},
/* 61 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)}, /* 61 */ {`"1.5" < `, nil, `[1:8] infix operator "<" requires two non-nil operands, got 1`},
/* 62 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)}, /* 62 */ {`"1.5" > `, nil, `[1:8] infix operator ">" requires two non-nil operands, got 1`},
/* 63 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)}, /* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
/* 64 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)}, /* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
/* 65 */ {"+1.5", float64(1.5), nil}, /* 65 */ {"+1.5", float64(1.5), nil},
/* 66 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)}, /* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
/* 67 */ {"4 / 0", nil, errors.New(`division by zero`)}, /* 67 */ {"4 / 0", nil, `division by zero`},
/* 68 */ {"4.0 / 0", nil, errors.New(`division by zero`)}, /* 68 */ {"4.0 / 0", nil, `division by zero`},
/* 69 */ {"4.0 / \n2", float64(2.0), nil}, /* 69 */ {"4.0 / \n2", float64(2.0), nil},
/* 70 */ {`123`, int64(123), nil}, /* 70 */ {`123`, int64(123), nil},
/* 71 */ {`1.`, float64(1.0), nil}, /* 71 */ {`1.`, float64(1.0), nil},
@@ -92,7 +91,7 @@ func TestGeneralParser(t *testing.T) {
/* 77 */ {`5 % 2`, int64(1), nil}, /* 77 */ {`5 % 2`, int64(1), nil},
/* 78 */ {`5 % (-2)`, int64(1), nil}, /* 78 */ {`5 % (-2)`, int64(1), nil},
/* 79 */ {`-5 % 2`, int64(-1), nil}, /* 79 */ {`-5 % 2`, int64(-1), nil},
/* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`)}, /* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, /* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, /* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, /* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
@@ -105,12 +104,12 @@ func TestGeneralParser(t *testing.T) {
/* 90 */ {`x=2 but x*10`, int64(20), nil}, /* 90 */ {`x=2 but x*10`, int64(20), nil},
/* 91 */ {`false and true`, false, nil}, /* 91 */ {`false and true`, false, nil},
/* 92 */ {`false and (x==2)`, false, nil}, /* 92 */ {`false and (x==2)`, false, nil},
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)}, /* 93 */ {`false and (x=2 but x==2) or x==2`, nil, `undefined variable or function "x"`},
/* 94 */ {`false or true`, true, nil}, /* 94 */ {`false or true`, true, nil},
/* 95 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)}, /* 95 */ {`false or (x==2)`, nil, `undefined variable or function "x"`},
/* 96 */ {`a=5; a`, int64(5), nil}, /* 96 */ {`a=5; a`, int64(5), nil},
/* 97 */ {`2=5`, nil, errors.New(`[1:2] left operand of "=" must be a variable or a collection's item`)}, /* 97 */ {`2=5`, nil, `[1:2] left operand of "=" must be a variable or a collection's item`},
/* 98 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable or a collection's item`)}, /* 98 */ {`2+a=5`, nil, `[1:3] left operand of "=" must be a variable or a collection's item`},
/* 99 */ {`2+(a=5)`, int64(7), nil}, /* 99 */ {`2+(a=5)`, int64(7), nil},
/* 100 */ {`x ?? "default"`, "default", nil}, /* 100 */ {`x ?? "default"`, "default", nil},
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil}, /* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
@@ -120,25 +119,31 @@ func TestGeneralParser(t *testing.T) {
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil}, /* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil}, /* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil}, /* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)}, /* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, `[1:34] case list in default clause`},
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil}, /* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil}, /* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)}, /* 111 */ {`10 ? {"a"} : {"b"}`, nil, `[1:3] no case catches the value (10) of the selection expression`},
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)}, /* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, `[1:22] selector-case outside of a selector context`},
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil}, /* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil}, /* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
/* 115 */ {`nil`, nil, nil}, /* 115 */ {`nil`, nil, nil},
/* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, /* 116 */ {`null`, nil, `undefined variable or function "null"`},
/* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, /* 117 */ {`{"key"}`, nil, "[1:8] expected `:`, got `}`"},
/* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, /* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
/* 119 */ {`{}`, &DictType{}, nil}, /* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil}, /* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil}, /* 121 */ {`1+1|2+0.5`, float64(2), nil},
/* 122 */ {`1.2()`, newFraction(6, 5), nil}, /* 122 */ {`1.2()`, newFraction(6, 5), nil},
/* 123 */ {`1|(2-2)`, nil, errors.New(`division by zero`)}, /* 123 */ {`1|(2-2)`, nil, `division by zero`},
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
/* 126 */ {`x ?! (x+1)`, nil, nil},
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 102) //runTestSuiteSpec(t, section, inputs, 130)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+9 -2
View File
@@ -9,8 +9,15 @@ import (
) )
func _TestImportPlugin(t *testing.T) { func _TestImportPlugin(t *testing.T) {
if err := importPlugin([]string{"test-resources"}, "json"); err != nil { t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
t.Errorf("importPlugin() failed: %v", err) t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
gotCount, gotErr := importPluginFromSearchPath("json")
if gotCount != 1 {
t.Errorf("Import count: got=%d, want=1", gotCount)
}
if gotErr != nil {
t.Errorf("importPlugin() failed: %v", gotErr)
} }
} }
+2 -3
View File
@@ -6,7 +6,6 @@ package expr
import ( import (
"errors" "errors"
"fmt"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@@ -69,9 +68,9 @@ func TestScanner(t *testing.T) {
// continue // continue
// } // }
if input.wantErr == nil { if input.wantErr == nil {
t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source)) t.Logf("[+]Test nr %2d -- %q", i+1, input.source)
} else { } else {
t.Log(fmt.Sprintf("[-]Test nr %2d -- %q", i+1, input.source)) t.Logf("[-]Test nr %2d -- %q", i+1, input.source)
} }
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
+95 -76
View File
@@ -26,7 +26,7 @@ const (
priSign priSign
priFact priFact
priIterValue priIterValue
priCoalesce priDefault
priIncDec priIncDec
priDot priDot
priValue priValue
@@ -53,21 +53,40 @@ type term struct {
evalFunc evalFuncType evalFunc evalFuncType
} }
func (self *term) String() string { func (s *term) Clone() (d *term) {
var children []*term
if s.children != nil {
children = make([]*term, len(s.children))
for i, c := range s.children {
children[i] = c.Clone()
}
}
d = &term{
tk: *s.tk.Clone(),
parent: s.parent,
children: children,
position: s.position,
priority: s.priority,
evalFunc: s.evalFunc,
}
return
}
func (term *term) String() string {
var sb strings.Builder var sb strings.Builder
self.toString(&sb) term.toString(&sb)
return sb.String() return sb.String()
} }
func (self *term) toString(sb *strings.Builder) { func (term *term) toString(sb *strings.Builder) {
if self.position == posLeaf { if term.position == posLeaf {
sb.WriteString(self.tk.String()) sb.WriteString(term.tk.String())
} else { } else {
sb.WriteByte('[') sb.WriteByte('[')
sb.WriteString(self.tk.String()) sb.WriteString(term.tk.String())
if self.children != nil { if term.children != nil {
sb.WriteByte('(') sb.WriteByte('(')
for i, c := range self.children { for i, c := range term.children {
if i > 0 { if i > 0 {
sb.WriteByte(' ') sb.WriteByte(' ')
} }
@@ -79,17 +98,17 @@ func (self *term) toString(sb *strings.Builder) {
} }
} }
func (self *term) getChildrenCount() (count int) { func (term *term) getChildrenCount() (count int) {
if self.position == posLeaf || self.children == nil { if term.position == posLeaf || term.children == nil {
count = 0 count = 0
} else { } else {
count = len(self.children) count = len(term.children)
} }
return return
} }
func (self *term) getRoom() (room int) { func (term *term) getRoom() (room int) {
switch self.position { switch term.position {
case posLeaf: case posLeaf:
room = 0 room = 0
case posInfix: case posInfix:
@@ -102,139 +121,139 @@ func (self *term) getRoom() (room int) {
return return
} }
func (self *term) isComplete() bool { func (term *term) isComplete() bool {
return self.getChildrenCount() == self.getRoom() return term.getChildrenCount() == term.getRoom()
} }
func (self *term) removeLastChild() (child *term) { func (term *term) removeLastChild() (child *term) {
if self.children != nil { if term.children != nil {
child = self.children[len(self.children)-1] child = term.children[len(term.children)-1]
self.children = self.children[0 : len(self.children)-1] term.children = term.children[0 : len(term.children)-1]
} else { } else {
panic("Can't get last child") panic("Can't get last child")
} }
return return
} }
func (self *term) isLeaf() bool { func (term *term) isLeaf() bool {
return self.position == posLeaf return term.position == posLeaf
} }
func (self *term) getPriority() termPriority { func (term *term) getPriority() termPriority {
return self.priority return term.priority
} }
func (self *term) setParent(parent *term) { func (term *term) setParent(parent *term) {
self.parent = parent term.parent = parent
if parent != nil && len(parent.children) < cap(parent.children) { if parent != nil && len(parent.children) < cap(parent.children) {
parent.children = append(parent.children, self) parent.children = append(parent.children, term)
} }
} }
func (self *term) symbol() Symbol { func (term *term) symbol() Symbol {
return self.tk.Sym return term.tk.Sym
} }
func (self *term) source() string { func (term *term) source() string {
return self.tk.source return term.tk.source
} }
func (self *term) value() any { func (term *term) value() any {
return self.tk.Value return term.tk.Value
} }
func (self *term) compute(ctx ExprContext) (v any, err error) { func (term *term) compute(ctx ExprContext) (v any, err error) {
if self.evalFunc == nil { if term.evalFunc == nil {
err = self.tk.Errorf("undefined eval-func for %q term", self.source()) err = term.Errorf("undefined eval-func for %q term", term.source())
} else { } else {
v, err = self.evalFunc(ctx, self) v, err = term.evalFunc(ctx, term)
} }
return return
} }
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) { // func (term *term) toInt(computedValue any, valueDescription string) (i int, err error) {
if index64, ok := computedValue.(int64); ok { // if index64, ok := computedValue.(int64); ok {
i = int(index64) // i = int(index64)
} else { // } else {
err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue) // err = term.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
} // }
return // return
} // }
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { func (term *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := TypeName(leftValue) leftType := TypeName(leftValue)
leftText := getFormatted(leftValue, Truncate) leftText := getFormatted(leftValue, Truncate)
rightType := TypeName(rightValue) rightType := TypeName(rightValue)
rightText := getFormatted(rightValue, Truncate) rightText := getFormatted(rightValue, Truncate)
return self.tk.Errorf( return term.tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q", "left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
leftText, leftType, leftText, leftType,
rightText, rightType, rightText, rightType,
self.source()) term.source())
} }
func (self *term) errIncompatibleType(value any) error { func (term *term) errIncompatibleType(value any) error {
return self.tk.Errorf( return term.tk.Errorf(
"prefix/postfix operator %q do not support operand '%v' [%s]", "prefix/postfix operator %q do not support operand '%v' [%s]",
self.source(), value, TypeName(value)) term.source(), value, TypeName(value))
} }
func (self *term) Errorf(template string, args ...any) (err error) { func (term *term) Errorf(template string, args ...any) (err error) {
err = self.tk.Errorf(template, args...) err = term.tk.Errorf(template, args...)
return return
} }
func (self *term) checkOperands() (err error) { func (term *term) checkOperands() (err error) {
switch self.position { switch term.position {
case posInfix: case posInfix:
if self.children == nil || len(self.children) != 2 || self.anyChildrenNil() { if term.children == nil || len(term.children) != 2 || term.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires two non-nil operands, got %d", self.source(), self.getChildrenCount()) err = term.tk.Errorf("infix operator %q requires two non-nil operands, got %d", term.source(), term.getChildrenCount())
} }
case posPrefix: case posPrefix:
if self.children == nil || len(self.children) != 1 || self.children[0] == nil { if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
err = self.tk.Errorf("prefix operator %q requires one not nil operand", self.tk.String()) err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
} }
case posPostfix: case posPostfix:
if self.children == nil || len(self.children) != 1 || self.anyChildrenNil() { if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
err = self.tk.Errorf("postfix operator %q requires one not nil operand", self.tk.String()) err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
} }
case posMultifix: case posMultifix:
if self.children == nil || len(self.children) < 3 || self.anyChildrenNil() { if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires at least three not operands, got %d", self.source(), self.getChildrenCount()) err = term.tk.Errorf("infix operator %q requires at least three not operands, got %d", term.source(), term.getChildrenCount())
} }
} }
return return
} }
func (self *term) anyChildrenNil() bool { func (term *term) anyChildrenNil() bool {
for _, child := range self.children { for _, child := range term.children {
if child == nil { if child == nil {
return true return true
} }
} }
return false return false
} }
func (self *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) { func (term *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) {
if err = self.checkOperands(); err == nil { if err = term.checkOperands(); err == nil {
if leftValue, err = self.children[0].compute(ctx); err == nil { if leftValue, err = term.children[0].compute(ctx); err == nil {
rightValue, err = self.children[1].compute(ctx) rightValue, err = term.children[1].compute(ctx)
} }
} }
return return
} }
func (self *term) evalPrefix(ctx ExprContext) (childValue any, err error) { func (term *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
if err = self.checkOperands(); err == nil { if err = term.checkOperands(); err == nil {
childValue, err = self.children[0].compute(ctx) childValue, err = term.children[0].compute(ctx)
} }
return return
} }
// NOTE Temporary solution to support function parameters with default value // NOTE Temporary solution to support function parameters with default value
func (self *term) forceChild(c *term) { func (t *term) forceChild(c *term) {
if self.children == nil { if t.children == nil {
self.children = make([]*term, 0, 1) t.children = make([]*term, 0, 1)
} }
self.children = append(self.children, c) t.children = append(t.children, c)
} }
+15
View File
@@ -0,0 +1,15 @@
ds={
"init":func(end){@end=end; @current=0 but true},
"current":func(){current},
"next":func(){
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
}
, "filter":func(item,index) { item > 0 }
, "map": func(item,index) { item * 2 }
}
/* Example
;
it=$(ds,3);
add(it)
*/
+22 -14
View File
@@ -28,12 +28,12 @@ func (tk *Token) DevString() string {
func (tk *Token) String() string { func (tk *Token) String() string {
if tk.Value != nil { if tk.Value != nil {
if s, ok := tk.Value.(string); ok { if s, ok := tk.Value.(string); ok {
return fmt.Sprintf("%q", s) return s //fmt.Sprintf("%q", s)
} else { } else {
return fmt.Sprintf("%v", tk.Value) return fmt.Sprintf("%v", tk.Value)
} }
} }
return fmt.Sprintf("%s", tk.source) return tk.source
} }
func NewToken(row, col int, sym Symbol, source string) *Token { func NewToken(row, col int, sym Symbol, source string) *Token {
@@ -51,6 +51,10 @@ func NewErrorToken(row, col int, err error) *Token {
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
} }
func (tk *Token) Clone() (c *Token) {
return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
}
func (tk *Token) IsEos() bool { func (tk *Token) IsEos() bool {
return tk.Sym == SymEos return tk.Sym == SymEos
} }
@@ -67,35 +71,39 @@ func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0 return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
} }
func (tk *Token) IsOneOfA(termSymbols ...Symbol) bool {
return slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsSymbol(sym Symbol) bool { func (tk *Token) IsSymbol(sym Symbol) bool {
return tk.Sym == sym return tk.Sym == sym
} }
func (self *Token) Errorf(template string, args ...any) (err error) { func (tk *Token) Errorf(template string, args ...any) (err error) {
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
return return
} }
func (self *Token) Error() (err error) { func (tk *Token) Error() (err error) {
if self.Sym == SymError { if tk.Sym == SymError {
if msg, ok := self.Value.(error); ok { if msg, ok := tk.Value.(error); ok {
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
} }
} }
return return
} }
func (self *Token) Errors(msg string) (err error) { func (tk *Token) Errors(msg string) (err error) {
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
return return
} }
func (self *Token) ErrorExpectedGot(symbol string) (err error) { func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self) err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, tk)
return return
} }
func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) { func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got) err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
return return
} }
+38
View File
@@ -6,7 +6,11 @@ package expr
import ( import (
"fmt" "fmt"
"os"
"os/user"
"path"
"reflect" "reflect"
"strings"
) )
func IsString(v any) (ok bool) { func IsString(v any) (ok bool) {
@@ -221,3 +225,37 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
} }
return result return result
} }
func ExpandPath(sourcePath string) (expandedPath string, err error) {
for expandedPath = os.ExpandEnv(sourcePath); expandedPath != sourcePath; expandedPath = os.ExpandEnv(sourcePath) {
sourcePath = expandedPath
}
if strings.HasPrefix(sourcePath, "~") {
var home, userName, remainder string
slashPos := strings.IndexRune(sourcePath, '/')
if slashPos > 0 {
userName = sourcePath[1:slashPos]
remainder = sourcePath[slashPos:]
} else {
userName = sourcePath[1:]
}
if len(userName) == 0 {
home, err = os.UserHomeDir()
if err != nil {
return
}
} else {
var userInfo *user.User
userInfo, err = user.Lookup(userName)
if err != nil {
return
}
home = userInfo.HomeDir
}
expandedPath = path.Join(home, remainder)
}
return
}