Compare commits

...

80 Commits

Author SHA1 Message Date
camoroso b99ad5def1 Merge branch 'iterator/dict' 2026-04-19 15:18:07 +02:00
camoroso 0d44b8697b doc: more details about iterators over dicts 2026-04-19 15:17:21 +02:00
camoroso 1a7e537921 more tests on iterator over dicts 2026-04-19 15:16:25 +02:00
camoroso 807df0f3a8 Iterator: added support for iterators over dictionaries 2026-04-19 15:08:14 +02:00
camoroso d7dd628fc9 doc: further details about iterator definition 2026-04-19 08:20:40 +02:00
camoroso 1f57ba28dd Doc: progress about builtin modules 2026-04-18 12:21:42 +02:00
camoroso 92bd366380 builtin-base: bool() supports lists and dicts, int() supports fractions 2026-04-18 12:20:05 +02:00
camoroso 7b93c5b4ac doc: begin of iterators description 2026-04-16 10:13:41 +02:00
camoroso 3ba8194ddb Expr.doc, a lot of fixes 2026-04-15 18:17:27 +02:00
camoroso 037565c41e New var() function added to the builtin set 2026-04-15 16:04:12 +02:00
camoroso f8d12b1a93 added TestGoFunction() to test Go functions 2026-04-15 16:03:41 +02:00
camoroso 518d4d8d65 simple-store.go: added function Init() 2026-01-03 09:11:43 +01:00
camoroso d64602cb00 builtin-string.go: fix return type of strLower() and strUpper() 2025-11-15 06:18:38 +01:00
camoroso 4709248828 string builtin: strUpper() and strLower() added 2025-11-13 20:53:07 +01:00
camoroso 5ecf81412e Doc: pre & post incremente/decrement 2025-01-05 12:53:50 +01:00
camoroso ff4db34f7b t_operator_test.go: test on -- and ++ prefix operators 2025-01-05 12:49:36 +01:00
camoroso 0f848071c2 New prefix operators ++ and -- 2025-01-04 18:12:38 +01:00
camoroso 6b3351b324 new prefix operator "!" as logic NOT 2025-01-04 17:47:59 +01:00
camoroso 760c1ee6da New bitwise XOR operator specified by symbol ^ (caret). Iterator dereference is now done by prefixed * (star) 2025-01-03 07:33:17 +01:00
camoroso 5ab6876ea1 term.go: new priority priDereference 2025-01-03 07:28:30 +01:00
camoroso 6268abda8c token.go: new member function Token.SetSymbol() 2025-01-03 07:27:49 +01:00
camoroso d25bd325b7 symbol-map.go: improved detection of incomplete operations 2025-01-03 06:32:55 +01:00
camoroso 01c04feea5 Doc: bitwise operators in the main operator table and special assignment operators table 2025-01-03 05:43:50 +01:00
camoroso 6fc689c46c Added a test to the context type 2025-01-03 05:40:24 +01:00
camoroso eccb0c4dc9 Added new special operators like &= and <<=.
Also made a litle change to scanner function moveOn(): now it moves on
the last char passed and only if there are more than one chars.
2024-12-29 19:26:02 +01:00
camoroso e43823740f Doc: added string splitter operator '/' 2024-12-28 19:22:26 +01:00
camoroso 526982a564 new string operator '/' 2024-12-28 19:16:45 +01:00
camoroso 252514943e parser.go: fix currentItem after parse a subexpr 2024-12-28 19:16:03 +01:00
camoroso 32686fac62 term.go: new member function errDivisionByZero() 2024-12-28 19:14:34 +01:00
camoroso 52a627c747 Edited according to preious commit 2024-12-28 09:28:12 +01:00
camoroso d91e7eb979 The list operator '<<' (append) and '>>' (prepend) have been replaced
with the new operators '<+' and '+>' respectively.
'<<' and '>>' are now used only for left and right binary shit
respectively. Further, their priority has been increased moving them to
a higher priority than that of the assignment operator.
2024-12-28 09:17:27 +01:00
camoroso cca3b76baa solved a number of problems highlighted by the syntax analyzer 2024-12-27 07:46:11 +01:00
camoroso 24e31997fc context-helpers.go: export functions no longer export control flags 2024-12-27 07:22:28 +01:00
camoroso 646710e180 builtin-base.go: new eval() function 2024-12-27 07:14:26 +01:00
camoroso b38327b841 t_builtin-string_test.go: corrected an undefinite article 2024-12-27 07:14:01 +01:00
camoroso fd912b2eb1 common-errors.go: undefinte article selection in error messages 2024-12-27 07:13:03 +01:00
camoroso 0e55f83d56 Forced the exlamation mark as a postfix operator 2024-12-26 08:57:14 +01:00
camoroso 4725145d1c Doc: changed fraction symbol and introduced binary operators 2024-12-25 07:43:06 +01:00
camoroso edf8818f51 New dedicated priority for binary operators between relational and sum ones 2024-12-25 07:41:08 +01:00
camoroso 6211be8a8f Completed transition of the symbol '|' from fraction to operator binary or. New fraction symbol is ':'.
Also, fixed and improved some parsing sections concerning collection indeces and ranges
2024-12-23 06:59:39 +01:00
camoroso f50ddf48db operator-range.go: range-term registered with symbol SymRange 2024-12-23 06:55:57 +01:00
camoroso 76e01f12d2 term.go: two error messages corrected 2024-12-23 06:53:37 +01:00
camoroso 406bced450 operator-sum.go: sum of two fraction fixed 2024-12-23 06:52:10 +01:00
camoroso 409dc86a92 symbol.go: SymRange added 2024-12-23 06:50:02 +01:00
camoroso 4184221428 symbol-map.go: changed symbol classification of some symbols like quotes and post-op 2024-12-23 06:49:17 +01:00
camoroso 8cf8b36a26 t_parser_test.go: replaced ~ with NOT 2024-12-19 15:36:16 +01:00
camoroso de87050188 scanner.go: removed SymTilde from DefaultTranslatios() -> It is not an alias for the SymNot symbol any more 2024-12-19 15:30:29 +01:00
camoroso a1ec0cc611 All assignment operators set the firstToken flag 2024-12-19 15:27:38 +01:00
camoroso 8e5550bfa7 New operator %= 2024-12-19 15:14:30 +01:00
camoroso 6ee21e10af New, more flexible, parser context datatype that includes and extends
the previous flags allowForest and allowVarRef.
Added binary operator (work in progress).
Better implementation of +=,-=,*=, and /= (new) operators.
2024-12-19 14:48:27 +01:00
camoroso 5c44532790 << && >>: left and right shift with integer operands 2024-12-07 07:06:08 +01:00
camoroso cb66c1ab19 symbol-map.go: removed unsed definitons 2024-10-13 08:44:21 +02:00
camoroso a28d24ba68 parser.go: improved terminal symbols thanks to new symbol-map.go functions 2024-10-13 08:42:55 +02:00
camoroso 523349a204 symbol-map.go: new file that helps to identify symbols by source and class 2024-10-13 08:41:30 +02:00
camoroso b185f1df3a token.go: added a few error functions 2024-10-13 08:39:56 +02:00
camoroso 5da5a61a42 Expr.doc: notes about function context 2024-10-05 05:30:22 +02:00
camoroso 6e9205abc4 t_funcs_test.go: A test add on parameters check about two params with the same name 2024-10-05 05:25:29 +02:00
camoroso f61004fb5d A test added on new implicit boolean cases in selector operator 2024-10-05 05:23:55 +02:00
camoroso 321030c8d3 parser.go: function parameter list can't specify same parameter more than once 2024-10-01 06:39:51 +02:00
camoroso 98fc89e84f operator-selector.go: Simplified selector for Boolean expressions 2024-10-01 06:37:35 +02:00
camoroso 778d00677d Doc: closure example 2024-09-18 20:48:12 +02:00
camoroso ba3dbb7f02 Doc: continuation 2024-09-16 06:52:29 +02:00
camoroso 7285109115 parser.go: number sign is now allowed after the assign operator 2024-09-16 06:49:26 +02:00
camoroso 4755774edd Doc: Fixed a lot of typos 2024-09-12 06:57:43 +02:00
camoroso d215d837f6 Reset() and Clean() have new, simpler signature 2024-09-12 05:44:29 +02:00
camoroso ad3c1e5a60 enhanced and simplified Reset(), Clean() and Next() methods 2024-09-09 15:23:07 +02:00
camoroso d6bf5ee500 common-type-names.go: TypeNil and TyperDict added 2024-09-09 15:16:42 +02:00
camoroso 4b176eb868 Fix function.go: CallFunctionByParams() dit not pass correctly received actual params 2024-08-23 10:29:57 +02:00
camoroso dceb31f542 CallFunction() has been replaced by three new functions:
CallFunctionByTerm(), CallFunctionByArgs() and CallFunctionByParams()
2024-08-02 06:39:33 +02:00
camoroso 075b0b5691 RegisterFunc() also returns the funcInfo object 2024-08-01 00:09:49 +02:00
camoroso 3c51b8d2ee Changed some function names and param types 2024-07-31 09:11:57 +02:00
camoroso a46753f453 Function buildActualParams moved from data-cursor.go ro function.go 2024-07-31 09:08:58 +02:00
camoroso 9070b5c9cc The function parameter model has been modified to support the passing of named parameters 2024-07-28 18:49:08 +02:00
camoroso ab06702e5e operator-post-inc.go: new post int decrement operator '--' 2024-07-24 06:39:35 +02:00
camoroso ffe1fa3aac op-assign expansion now end at ']' and '}' too 2024-07-24 06:37:57 +02:00
camoroso 1a1a475dd8 Added support to op-assign operators like '+='.
This feature is implemented by expansion, not by dedicated operators, e.g. a*=2+x is exapanded as a=a*(2+x)
2024-07-23 15:35:57 +02:00
camoroso 463e3634ba scanner.go: New function UnreadToken().
Currently it only supports one staging level.
2024-07-23 15:32:25 +02:00
camoroso 5f8ca47ef0 term.go: New function Clone() 2024-07-23 15:27:50 +02:00
camoroso 837b887490 token.go: New functions Clone() and IsOneOfA() 2024-07-23 15:27:36 +02:00
camoroso c76e1d3c8e symbol.go: New symbol '*=' 2024-07-23 15:24:54 +02:00
77 changed files with 4925 additions and 1173 deletions
+6 -6
View File
@@ -45,19 +45,19 @@ func (expr *ast) String() string {
func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens {
if err = expr.addToken(tk); err != nil {
if _, err = expr.addToken(tk); err != nil {
break
}
}
return
}
func (expr *ast) addToken(tk *Token) (err error) {
_, err = expr.addToken2(tk)
return
}
// func (expr *ast) addToken(tk *Token) (err error) {
// _, err = expr.addToken2(tk)
// return
// }
func (expr *ast) addToken2(tk *Token) (t *term, err error) {
func (expr *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil {
err = expr.addTerm(t)
} else {
+16 -17
View File
@@ -12,19 +12,14 @@ type exprFunctor struct {
defCtx ExprContext
}
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
// }
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
return functor.params
}
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
var defCtx ExprContext
if ctx != nil {
// if ctx.GetParent() != nil {
// defCtx = ctx.Clone()
// defCtx.SetParent(ctx)
// } else {
defCtx = ctx
// }
}
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
}
@@ -37,14 +32,10 @@ func (functor *exprFunctor) GetDefinitionContext() ExprContext {
return functor.defCtx
}
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
// if functor.defCtx != nil {
// ctx.Merge(functor.defCtx)
// }
for i, p := range functor.params {
if i < len(args) {
arg := args[i]
func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var missing []string
for _, p := range functor.params {
if arg, exists := args[p.Name()]; exists {
if funcArg, ok := arg.(Functor); ok {
paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
@@ -52,9 +43,17 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
ctx.UnsafeSetVar(p.Name(), arg)
}
} else {
ctx.UnsafeSetVar(p.Name(), nil)
if missing == nil {
missing = make([]string, 0, 1)
}
missing = append(missing, p.Name())
// ctx.UnsafeSetVar(p.Name(), nil)
}
}
if missing != nil {
err = ErrMissingParams(name, missing)
} else {
result, err = functor.expr.Eval(ctx)
}
return
}
+1 -1
View File
@@ -18,6 +18,6 @@ func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+96 -33
View File
@@ -8,55 +8,60 @@ import (
"fmt"
"math"
"strconv"
"strings"
)
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = args[0] == nil
const (
ParamDenominator = "denominator"
)
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = args[ParamValue] == nil
return
}
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsInteger(args[0])
func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsInteger(args[ParamValue])
return
}
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFloat(args[0])
func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFloat(args[ParamValue])
return
}
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsBool(args[0])
func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsBool(args[ParamValue])
return
}
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsString(args[0])
func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsString(args[ParamValue])
return
}
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFract(args[0])
func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFract(args[ParamValue])
return
}
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsRational(args[0])
func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsRational(args[ParamValue])
return
}
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsList(args[0])
func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsList(args[ParamValue])
return
}
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsDict(args[0])
func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
result = IsDict(args[ParamValue])
return
}
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = (v != 0)
case *FractionType:
@@ -67,14 +72,18 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
result = v
case string:
result = len(v) > 0
case *ListType:
result = len(*v) > 0
case *DictType:
result = len(*v) > 0
default:
err = ErrCantConvert(name, v, "bool")
}
return
}
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = v
case float64:
@@ -90,14 +99,16 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if i, err = strconv.Atoi(v); err == nil {
result = int64(i)
}
case *FractionType:
result = int64(v.num / v.den)
default:
err = ErrCantConvert(name, v, "int")
}
return
}
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = float64(v)
case float64:
@@ -121,8 +132,8 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return
}
func stringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
result = strconv.FormatInt(v, 10)
case float64:
@@ -147,18 +158,18 @@ func stringFunc(ctx ExprContext, name string, args []any) (result any, err error
return
}
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
switch v := args[0].(type) {
func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) {
case int64:
var den int64 = 1
if len(args) > 1 {
var ok bool
if den, ok = args[1].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[1])
if den, ok = args[ParamDenominator].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[ParamDenominator])
} else if den == 0 {
err = ErrFuncDivisionByZero(name)
}
}
if err == nil {
result = newFraction(v, den)
}
@@ -184,6 +195,49 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
// return
// }
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok {
var expr Expr
parser := NewParser()
if ctx == nil {
ctx = NewSimpleStore()
}
r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations())
if expr, err = parser.Parse(scanner); err == nil {
CtrlEnable(ctx, control_export_all)
result, err = expr.Eval(ctx)
}
} else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
return
}
func varFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var varName string
var ok bool
if varName, ok = args[ParamName].(string); !ok {
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
}
if result, ok = args[ParamValue]; ok && result != nil {
ctx.GetParent().UnsafeSetVar(varName, result)
// } else {
// err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
// }
} else if result, ok = ctx.GetVar(varName); !ok {
err = ErrUnknownVar(name, varName)
}
return
}
//// import
func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{
NewFuncParam(ParamValue),
@@ -205,7 +259,16 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
NewFuncParam(ParamValue),
NewFuncParamFlagDef("denominator", PfDefault, 1),
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
})
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamSource),
})
ctx.RegisterFunc("var", NewGolangFunctor(varFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamName),
NewFuncParamFlagDef(ParamValue, PfDefault, nil),
})
}
+14 -8
View File
@@ -21,19 +21,25 @@ func getStdout(ctx ExprContext) io.Writer {
return w
}
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var n int
if n, err = fmt.Fprint(getStdout(ctx), args...); err == nil {
result = int64(n)
func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0
if v, exists := args[ParamItem]; exists && v != nil {
argv := v.([]any)
n, err = fmt.Fprint(getStdout(ctx), argv...)
}
result = int64(n)
return
}
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var n int
if n, err = fmt.Fprintln(getStdout(ctx), args...); err == nil {
result = int64(n)
func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0
if v, exists := args[ParamItem]; exists && v != nil {
argv := v.([]any)
n, err = fmt.Fprintln(getStdout(ctx), argv...)
} else {
n, err = fmt.Fprintln(getStdout(ctx))
}
result = int64(n)
return
}
+8 -5
View File
@@ -9,18 +9,21 @@ import (
"os"
)
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func importFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return importGeneral(ctx, name, args)
}
func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
CtrlEnable(ctx, control_export_all)
return importGeneral(ctx, name, args)
}
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
result, err = doImport(ctx, name, dirList, NewArrayIterator(args))
if v, exists := args[ParamFilepath]; exists && v != nil {
argv := v.([]any)
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
}
return
}
@@ -41,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
var expr *ast
scanner := NewScanner(file, DefaultTranslations())
parser := NewParser()
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
result, err = expr.Eval(ctx)
}
if err != nil {
+16 -15
View File
@@ -15,25 +15,26 @@ const (
iterVarStatus = "status"
)
func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) {
func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Functor, err error) {
var ok bool
if it, ok = args[0].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[0], TypeName(args[0]))
if it, ok = args[ParamIterator].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
return
}
if len(args) > 1 {
if op, ok = args[1].(Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[1], TypeName(args[1]))
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
}
if len(args) > 2 {
var vars *DictType
if vars, ok = args[2].(*DictType); !ok || vars == nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[2], TypeName(args[2]))
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 {
@@ -41,17 +42,17 @@ func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, er
}
}
}
}
return
}
func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var it Iterator
var ok bool
var op Functor
var v any
var usingDefaultOp = false
var params []any
var params map[string]any
var item any
localCtx := ctx.Clone()
@@ -66,12 +67,12 @@ func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
for item, err = it.Next(); err == nil; item, err = it.Next() {
if usingDefaultOp {
params = []any{item}
params = map[string]any{ParamItem: []any{item}}
} else {
params = []any{it.Index(), item}
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
}
if v, err = op.Invoke(localCtx, iterParamOperator, params); err != nil {
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
+8 -6
View File
@@ -86,8 +86,9 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
return
}
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1)
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any)
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
return
}
@@ -161,17 +162,18 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
return
}
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1)
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any)
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
return
}
func ImportMathFuncs(ctx ExprContext) {
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
})
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
})
}
+36 -29
View File
@@ -11,6 +11,10 @@ import (
"os"
)
const (
osLimitCh = "limitCh"
)
type osHandle interface {
getFile() *os.File
}
@@ -61,8 +65,8 @@ func errInvalidFileHandle(funcName string, v any) error {
}
}
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func createFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -73,8 +77,8 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return
}
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
@@ -85,8 +89,8 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
return
}
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -97,13 +101,13 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return
}
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
if handle, ok = args[0].(osHandle); !ok {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
@@ -124,18 +128,21 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
return
}
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
if handle, ok = args[0].(osHandle); !ok {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
if w, ok := handle.(*osWriter); ok {
result, err = fmt.Fprint(w.writer, args[1:]...)
if v, exists := args[ParamItem]; exists {
argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...)
}
} else {
invalidFileHandle = handle
}
@@ -147,21 +154,21 @@ func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, er
return
}
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
var limit byte = '\n'
var v string
if s, ok := args[1].(string); ok && len(s) > 0 {
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
limit = s[0]
}
@@ -187,14 +194,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err
return
}
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
invalidFileHandle = args[ParamHandle]
}
if handle != nil {
@@ -214,34 +221,34 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any,
}
func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParam(ParamHandle),
})
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
NewFuncParam(ParamHandle),
NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""),
})
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
NewFuncParam(ParamHandle),
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
})
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParam(ParamHandle),
})
}
+90 -42
View File
@@ -10,6 +10,10 @@ import (
"strings"
)
const (
strParamOther = "other"
)
// --- Start of function definitions
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
var sb strings.Builder
@@ -32,45 +36,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return
}
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
// if len(args) < 1 {
// return nil, errMissingRequiredParameter(name, paramSeparator)
// }
if sep, ok := args[0].(string); ok {
if len(args) == 1 {
result = ""
} else if len(args) == 2 {
if ls, ok := args[1].(*ListType); ok {
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if sep, ok := args[ParamSeparator].(string); ok {
if v, exists := args[ParamItem]; exists {
argv := v.([]any)
if len(argv) == 1 {
if ls, ok := argv[0].(*ListType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if it, ok := args[1].(Iterator); ok {
} else if it, ok := argv[0].(Iterator); ok {
result, err = doJoinStr(name, sep, it)
} else if s, ok := argv[0].(string); ok {
result = s
} else {
err = ErrInvalidParameterValue(name, ParamParts, args[1])
err = ErrInvalidParameterValue(name, ParamItem, v)
}
} else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
}
}
} else {
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0])
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
}
return
}
func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var start = 0
var count = -1
var source string
var ok bool
if source, ok = args[0].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
if start, err = ToGoInt(args[1], name+"()"); err != nil {
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil {
return
}
if count, err = ToGoInt(args[2], name+"()"); err != nil {
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
return
}
@@ -86,81 +90,99 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
return
}
func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source string
var ok bool
if source, ok = args[0].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
result = strings.TrimSpace(source)
return
}
func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var source string
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, prefix string
var ok bool
result = false
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
for i, targetSpec := range args[1:] {
if prefix, ok = args[ParamPrefix].(string); !ok {
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
}
if strings.HasPrefix(source, prefix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break
}
}
}
return
}
func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var source string
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, suffix string
var ok bool
result = false
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
for i, targetSpec := range args[1:] {
if suffix, ok = args[ParamSuffix].(string); !ok {
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
}
if strings.HasPrefix(source, suffix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
break
}
}
}
return
}
func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var source, sep string
var count int = -1
var parts []string
var ok bool
if source, ok = args[0].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
if source, ok = args[ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
if sep, ok = args[1].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1])
if sep, ok = args[ParamSeparator].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
}
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt()
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt()
count = int(count64)
} else {
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2])
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount])
}
if count > 0 {
@@ -178,6 +200,24 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
return
}
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok {
result = strings.ToUpper(source)
} else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
return
}
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok {
result = strings.ToLower(source)
} else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
}
return
}
// --- End of function definitions
// Import above functions in the context
@@ -206,13 +246,21 @@ func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource),
NewFuncParam(ParamPrefix),
NewFuncParamFlag("other "+ParamPrefix, PfRepeat),
NewFuncParamFlag(strParamOther, PfRepeat),
})
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource),
NewFuncParam(ParamSuffix),
NewFuncParamFlag("other "+ParamSuffix, PfRepeat),
NewFuncParamFlag(strParamOther, PfRepeat),
})
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamSource),
})
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamSource),
})
}
+33 -3
View File
@@ -6,8 +6,13 @@ package expr
import (
"fmt"
"strings"
)
func ErrMissingParams(funcName string, missing []string) (err error) {
return fmt.Errorf("%s(): missing params -- %s", funcName, strings.Join(missing, ", "))
}
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
@@ -17,8 +22,8 @@ func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error
return
}
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
return
}
@@ -50,8 +55,33 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
}
func undefArticle(s string) (article string) {
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
article = "an"
} else {
article = "a"
}
return
}
func prependUndefArticle(s string) (result string) {
return undefArticle(s) + " " + s
}
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)
var artWantType, artGotType string
gotType := TypeName(paramValue)
artGotType = prependUndefArticle(gotType)
artWantType = prependUndefArticle(paramType)
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
}
func ErrUnknownParam(funcName, paramName string) error {
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
}
func ErrUnknownVar(funcName, varName string) error {
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
}
// --- Operator errors
+4
View File
@@ -5,8 +5,10 @@
package expr
const (
ParamArgs = "args"
ParamCount = "count"
ParamItem = "item"
ParamIndex = "index"
ParamParts = "parts"
ParamSeparator = "separator"
ParamSource = "source"
@@ -19,6 +21,8 @@ const (
ParamEllipsis = "..."
ParamFilepath = "filepath"
ParamDirpath = "dirpath"
ParamHandle = "handle"
ParamResource = "resource"
ParamIterator = "iterator"
)
+3 -1
View File
@@ -6,15 +6,17 @@ package expr
const (
TypeAny = "any"
TypeNil = "nil"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeHandle = "handle"
TypeFileHandle = "file-handle"
TypeInt = "integer"
TypeItem = "item"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeDict = "dict"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)
+1 -1
View File
@@ -29,7 +29,7 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
// Export variables
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
// fmt.Printf("\tExporting %q\n", refName)
refValue, _ := sourceCtx.GetVar(refName)
exportVar(destCtx, refName, refValue)
+111 -39
View File
@@ -6,12 +6,14 @@ package expr
import (
"io"
"slices"
)
type dataCursor struct {
ds map[string]Functor
ctx ExprContext
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
// cursorValid bool // true if resource is nil or if clean has not yet been called
index int
count int
current any
@@ -26,6 +28,7 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
dc = &dataCursor{
ds: ds,
initState: true,
// cursorValid: true,
index: -1,
count: 0,
current: nil,
@@ -36,7 +39,6 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
}
//dc.Next()
return
}
@@ -77,7 +79,7 @@ func (dc *dataCursor) String() string {
}
func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == IndexName
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
if !exists {
f, ok := dc.ds[name]
exists = ok && isFunctor(f)
@@ -85,64 +87,86 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
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 {
value = int64(dc.Index())
} else if name == CleanName {
err = dc.Clean()
} else if name == ResetName {
err = dc.Reset()
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc {
value, err = dc.Clean()
} else if functor == dc.resetFunc {
value, err = dc.Reset()
} else {
ctx := cloneContext(dc.ctx)
value, err = functor.Invoke(ctx, name, []any{})
value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx)
}
} else {
err = errNoOperation(name)
}
return
}
func (dc *dataCursor) Reset() (success bool, err error) {
// func (dc *dataCursor) Reset() (err error) {
// if dc.resetFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
// exportObjects(dc.ctx, ctx)
// dc.index = -1
// dc.count = 0
// dc.initState = true
// dc.current = nil
// dc.lastErr = nil
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(ResetName)
// }
// return
// }
func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
_, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource})
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
exportObjects(dc.ctx, ctx)
}
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
//dc.Next()
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(ResetName)
}
success = err == nil
return
}
func (dc *dataCursor) Clean() (success bool, err error) {
func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
_, err = dc.cleanFunc.Invoke(ctx, CleanName, []any{dc.resource})
// dc.resource = nil
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(CleanName)
}
success = err == nil
dc.lastErr = io.EOF
return
}
// func (dc *dataCursor) Clean() (err error) {
// if dc.cleanFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
// exportObjects(dc.ctx, ctx)
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(CleanName)
// }
// return
// }
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
dc.init()
@@ -158,7 +182,9 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
var v any
var ok bool
ctx := cloneContext(dc.ctx)
if v, err = filter.Invoke(ctx, FilterName, []any{item, dc.index}); err == nil && v != nil {
actualParams := bindActualParams(filter, []any{item, dc.index})
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil {
if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
@@ -168,7 +194,8 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx)
mappedItem, err = mapper.Invoke(ctx, MapName, []any{item, dc.index})
actualParams := bindActualParams(mapper, []any{item, dc.index})
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
return
}
@@ -186,14 +213,15 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
return
}
current = dc.current
if dc.resource != nil {
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx)
dc.index++
if item, dc.lastErr = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource, dc.index}); dc.lastErr == nil {
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
@@ -218,12 +246,56 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
dc.index--
dc.Clean()
}
} else {
dc.lastErr = errInvalidDataSource()
}
return
}
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
// if dc.initState {
// dc.init()
// } else if err = dc.lastErr; err != nil {
// return
// }
// current = dc.current
// if dc.resource != nil {
// filter := dc.ds[FilterName]
// mapper := dc.ds[MapName]
// var item any
// for item == nil && dc.lastErr == nil {
// ctx := cloneContext(dc.ctx)
// dc.index++
// actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
// if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
// if item == nil {
// dc.lastErr = io.EOF
// } else {
// accepted := true
// if filter != nil {
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
// item = nil
// }
// }
// if accepted {
// dc.count++
// }
// if item != nil && mapper != nil {
// item, dc.lastErr = dc.mapItem(mapper, item)
// }
// }
// }
// exportObjects(dc.ctx, ctx)
// }
// dc.current = item
// if dc.lastErr != nil {
// dc.index--
// dc.Clean()
// }
// } else {
// dc.lastErr = errInvalidDataSource()
// }
// return
// }
func (dc *dataCursor) Index() int {
return dc.index - 1
}
+209
View File
@@ -0,0 +1,209 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"strings"
)
type dictIterMode int
const (
dictIterModeKeys dictIterMode = iota
dictIterModeValues
dictIterModeItems
)
type DictIterator struct {
a *DictType
count int
index int
keys []any
iterMode dictIterMode
}
type sortType int
const (
sortTypeNone sortType = iota
sortTypeAsc
sortTypeDesc
sortTypeDefault = sortTypeAsc
)
func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
it.keys = make([]any, 0, len(m))
if sort == sortTypeNone {
for keyAny := range m {
it.keys = append(it.keys, keyAny)
}
} else {
scalarMap := make(map[string]any, len(m))
scalerKeys := make([]string, 0, len(m))
for keyAny := range m {
keyStr := fmt.Sprint(keyAny)
scalarMap[keyStr] = keyAny
scalerKeys = append(scalerKeys, keyStr)
}
switch sort {
case sortTypeAsc:
slices.Sort(scalerKeys)
case sortTypeDesc:
slices.Sort(scalerKeys)
slices.Reverse(scalerKeys)
}
for _, keyStr := range scalerKeys {
it.keys = append(it.keys, scalarMap[keyStr])
}
}
}
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone
var s string
it = &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
if len(args) > 0 {
if s, err = ToGoString(args[0], "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
case "d", "desc":
sortType = sortTypeDesc
case "n", "none", "nosort", "no-sort":
sortType = sortTypeNone
case "", "default":
sortType = sortTypeDefault
default:
err = fmt.Errorf("invalid sort type %q", s)
}
if err == nil && len(args) > 1 {
if s, err = ToGoString(args[1], "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
it.iterMode = dictIterModeKeys
case "v", "value", "values":
it.iterMode = dictIterModeValues
case "i", "item", "items":
it.iterMode = dictIterModeItems
case "", "default":
it.iterMode = dictIterModeKeys
default:
err = fmt.Errorf("invalid iteration mode %q", s)
}
}
}
}
}
it.makeKeys(*dict, sortType)
return
}
func NewMapIterator(m map[any]any) (it *DictIterator) {
it = &DictIterator{a: (*DictType)(&m), count: 0, index: -1, keys: nil}
it.makeKeys(m, sortTypeNone)
return
}
func (it *DictIterator) String() string {
var l = 0
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$(#%d)", l)
}
func (it *DictIterator) TypeName() string {
return "DictIterator"
}
func (it *DictIterator) HasOperation(name string) bool {
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
yes := slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName, KeyName, ValueName}, name)
return yes
}
func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case NextName:
v, err = it.Next()
case ResetName:
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
v, err = it.Current()
case CountName:
v = it.count
case KeyName:
if it.index >= 0 && it.index < len(it.keys) {
v = it.keys[it.index]
} else {
err = io.EOF
}
case ValueName:
if it.index >= 0 && it.index < len(it.keys) {
a := *(it.a)
v = a[it.keys[it.index]]
} else {
err = io.EOF
}
default:
err = errNoOperation(name)
}
return
}
func (it *DictIterator) Current() (item any, err error) {
if it.index >= 0 && it.index < len(it.keys) {
switch it.iterMode {
case dictIterModeKeys:
item = it.keys[it.index]
case dictIterModeValues:
a := *(it.a)
item = a[it.keys[it.index]]
case dictIterModeItems:
a := *(it.a)
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
item = newList(pair)
}
} else {
err = io.EOF
}
return
}
func (it *DictIterator) Next() (item any, err error) {
it.index++
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *DictIterator) Index() int {
return it.index
}
func (it *DictIterator) Count() int {
return it.count
}
func (it *DictIterator) Reset() error {
it.index = -1
it.count = 0
return nil
}
func (it *DictIterator) Clean() error {
return nil
}
+1 -1
View File
@@ -33,7 +33,7 @@ func NewDict(dictAny map[any]any) (dict *DictType) {
}
func newDict(dictAny map[any]*term) (dict *DictType) {
// TODO Change wi a call to NewDict()
// TODO Change with a call to NewDict()
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
+913 -139
View File
File diff suppressed because it is too large Load Diff
+1496 -203
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -7,7 +7,6 @@ package expr
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
@@ -24,7 +23,7 @@ type ExprContext interface {
DeleteFunc(funcName string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
Call(name string, args map[string]any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
}
+3 -2
View File
@@ -7,7 +7,7 @@ package expr
// ---- Functor interface
type Functor interface {
Typer
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
@@ -32,7 +32,8 @@ type ExprFunc interface {
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ParamSpec(paramName string) ExprFuncParam
ReturnType() string
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
PrepareCall(name string, actualParams map[string]any) (err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
}
+14 -12
View File
@@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
} else if s[0] == '+' {
s = s[1:]
}
// if strings.HasSuffix(s, "()") {
// s = s[0 : len(s)-2]
// }
// if strings.HasSuffix(s, "()") {
// s = s[0 : len(s)-2]
// }
s = strings.TrimSuffix(s, "()")
parts = strings.SplitN(s, ".", 2)
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
@@ -124,36 +124,38 @@ func (f *FractionType) String() string {
func (f *FractionType) ToString(opt FmtOpt) string {
var sb strings.Builder
if opt&MultiLine == 0 {
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
} else {
var s, num string
var sign, num string
if f.num < 0 && opt&TTY == 0 {
num = strconv.FormatInt(-f.num, 10)
s = "-"
sign = "-"
} else {
num = strconv.FormatInt(f.num, 10)
}
den := strconv.FormatInt(f.den, 10)
size := max(len(num), len(den))
if opt&TTY != 0 {
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
sb.WriteString(sNum)
} else {
if len(s) > 0 {
if len(sign) > 0 {
sb.WriteString(" ")
}
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
sb.WriteByte('\n')
if len(s) > 0 {
sb.WriteString(s)
if len(sign) > 0 {
sb.WriteString(sign)
sb.WriteByte(' ')
}
sb.WriteString(strings.Repeat("-", size))
sb.WriteByte('\n')
if len(s) > 0 {
if len(sign) > 0 {
sb.WriteString(" ")
}
}
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
sb.WriteString(sDen)
}
return sb.String()
+151 -21
View File
@@ -6,11 +6,12 @@ package expr
import (
"fmt"
"strconv"
"strings"
)
// ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
// ---- Common functor definition
type baseFunctor struct {
@@ -137,10 +138,6 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
return info, nil
}
// func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
// return newFuncInfo("unnamed", functor, returnType, params)
// }
func (info *funcInfo) Params() []ExprFuncParam {
return info.formalParams
}
@@ -216,37 +213,133 @@ func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
return
}
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
passedCount := len(*varActualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
for i := passedCount; i < len(info.formalParams); i++ {
p := info.formalParams[i]
for i, tree := range callTerm.children {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.compute(paramCtx); err != nil {
break
}
if paramName, namedParam := getAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, len(callTerm.children)-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
*varActualParams = append(*varActualParams, p.DefaultValue())
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
}
if err == nil {
ctx = info.AllocContext(parentCtx)
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
// ----- Call a function ---
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
var ctx ExprContext
if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
func getAssignVarName(t *term) (name string, ok bool) {
if ok = t.symbol() == SymEqual; ok {
name = t.children[0].source()
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.Invoke(ctx, name, actualParams)
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
actualParams = bindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
@@ -254,3 +347,40 @@ func CallFunction(parentCtx ExprContext, name string, actualParams []any) (resul
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func bindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+14 -1
View File
@@ -55,7 +55,20 @@ func GetLocalFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool)
}
return
}
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
// if len(name) > 0 {
// if item, exists = GetLocalFuncInfo(ctx, name); exists {
// ownerCtx = ctx
// } else if item, exists = globalCtx.GetFuncInfo(name); exists {
// ownerCtx = globalCtx
// }
// }
item, exists, _ = GetFuncInfoAndOwner(ctx, name)
return
}
func GetFuncInfoAndOwner(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
if len(name) > 0 {
if item, exists = GetLocalFuncInfo(ctx, name); exists {
ownerCtx = ctx
+5 -1
View File
@@ -1,3 +1,7 @@
module git.portale-stac.it/go-pkg/expr
go 1.21.6
go 1.22.0
toolchain go1.23.3
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
+4
View File
@@ -0,0 +1,4 @@
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+10 -6
View File
@@ -5,7 +5,7 @@
package expr
import (
"errors"
// "errors"
"fmt"
)
@@ -21,6 +21,8 @@ const (
CountName = "count"
FilterName = "filter"
MapName = "map"
KeyName = "key"
ValueName = "value"
)
type Iterator interface {
@@ -29,18 +31,20 @@ type Iterator interface {
Current() (item any, err error)
Index() int
Count() int
HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error)
}
type ExtIterator interface {
Iterator
HasOperation(name string) bool
CallOperation(name string, args []any) (value any, err error)
Reset() error
Clean() error
}
func errNoOperation(name string) error {
return fmt.Errorf("no %s() function defined in the data-source", name)
}
func errInvalidDataSource() error {
return errors.New("invalid data-source")
}
// func errInvalidDataSource() error {
// return errors.New("invalid data-source")
// }
+10 -4
View File
@@ -94,12 +94,14 @@ func (it *ListIterator) HasOperation(name string) bool {
return yes
}
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case NextName:
v, err = it.Next()
case ResetName:
v, err = it.Reset()
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
@@ -147,8 +149,12 @@ func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (bool, error) {
func (it *ListIterator) Reset() error {
it.index = it.start - it.step
it.count = 0
return true, nil
return nil
}
func (it *ListIterator) Clean() error {
return nil
}
+18 -12
View File
@@ -21,20 +21,26 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
}
// -------- eval func call
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
// name, _ := opTerm.tk.Value.(string)
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
// for i, tree := range opTerm.children {
// var param any
// if param, err = tree.compute(ctx); err != nil {
// break
// }
// params[i] = param
// }
// if err == nil {
// v, err = CallFunction(ctx, name, params)
// }
// return
// }
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := opTerm.tk.Value.(string)
params := make([]any, len(opTerm.children), len(opTerm.children)+5)
for i, tree := range opTerm.children {
var param any
if param, err = tree.compute(ctx); err != nil {
break
}
params[i] = param
}
if err == nil {
v, err = CallFunction(ctx, name, params)
}
v, err = CallFunctionByTerm(ctx, name, opTerm)
return
}
+19 -19
View File
@@ -11,22 +11,6 @@ import (
// -------- iterator term
// func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
// tk.Sym = SymIterator
// children := make([]*term, 0, 1+len(args))
// children = append(children, dsTerm)
// children = append(children, args...)
// return &term{
// tk: *tk,
// parent: nil,
// children: children,
// position: posLeaf,
// priority: priValue,
// evalFunc: evalIterator,
// }
// }
func newIteratorTerm(tk *Token, args []*term) *term {
tk.Sym = SymIterator
return &term{
@@ -102,12 +86,15 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
return
}
err = nil
if ds != nil {
if len(ds) > 0 {
var dc *dataCursor
dcCtx := ctx.Clone()
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any
var resource any
@@ -119,16 +106,29 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
args = []any{}
}
actualParams := bindActualParams(initFunc, args)
initCtx := ctx.Clone()
if resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil {
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
return
}
dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} else {
dc = NewDataCursor(dcCtx, ds, nil)
}
v = dc
} else {
if dictIt, ok := firstChildValue.(*DictType); ok {
var args []any
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
v, err = NewDictIterator(dictIt, args)
}
} else {
err = opTerm.children[0].Errorf("the data-source must be a dictionary")
}
}
} else if list, ok := firstChildValue.(*ListType); ok {
var args []any
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
+1 -1
View File
@@ -25,7 +25,7 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
var exists bool
name := opTerm.source()
if v, exists = GetVar(ctx, name); !exists {
if info, exists, _ := GetFuncInfo(ctx, name); exists {
if info, exists := GetFuncInfo(ctx, name); exists {
v = info.Functor()
} else {
err = fmt.Errorf("undefined variable or function %q", name)
+115
View File
@@ -99,7 +99,122 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
//-------- assign term
func newOpAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priAssign,
evalFunc: evalOpAssign,
}
}
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
var collectionValue, keyListValue, keyValue any
var keyList *ListType
var ok bool
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
return
}
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
return
}
if keyValue = (*keyList)[0]; keyValue == nil {
err = keyListTerm.Errorf("index/key is nil")
return
}
switch collection := collectionValue.(type) {
case *ListType:
if index, ok := keyValue.(int64); ok {
value = (*collection)[index]
} else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
}
case *DictType:
value = (*collection)[keyValue]
default:
err = collectionTerm.Errorf("collection expected")
}
return
}
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
if leftTerm.symbol() == SymIndex {
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
} else {
value, _ = ctx.GetVar(leftTerm.source())
}
return
}
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue, leftValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
leftSym := leftTerm.symbol()
if leftSym != SymVariable && leftSym != SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
return
}
rightChild := opTerm.children[1]
if rightValue, err = rightChild.compute(ctx); err == nil {
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
switch opTerm.symbol() {
case SymPlusEqual:
v, err = sumValues(opTerm, leftValue, rightValue)
case SymMinusEqual:
v, err = diffValues(opTerm, leftValue, rightValue)
case SymStarEqual:
v, err = mulValues(opTerm, leftValue, rightValue)
case SymSlashEqual:
v, err = divValues(opTerm, leftValue, rightValue)
case SymPercEqual:
v, err = remainderValues(opTerm, leftValue, rightValue)
case SymAmpersandEqual:
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
case SymVertBarEqual:
v, err = bitwiseOr(opTerm, leftValue, rightValue)
case SymCaretEqual:
v, err = bitwiseXor(opTerm, leftValue, rightValue)
case SymDoubleLessEqual:
v, err = bitLeftShift(opTerm, leftValue, rightValue)
case SymDoubleGreaterEqual:
v, err = bitRightShift(opTerm, leftValue, rightValue)
default:
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
}
if err == nil {
err = assignValue(ctx, leftTerm, v)
}
}
}
return
}
// init
func init() {
registerTermConstructor(SymEqual, newAssignTerm)
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
registerTermConstructor(SymStarEqual, newOpAssignTerm)
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
registerTermConstructor(SymPercEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
}
+154
View File
@@ -0,0 +1,154 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-bitwise.go
package expr
//-------- Bitwise NOT term
func newBitwiseNotTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priBitwiseNot,
evalFunc: evalBitwiseNot,
}
}
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) {
var value any
if value, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if IsInteger(value) {
i, _ := value.(int64)
v = ^i
} else {
err = opTerm.errIncompatibleType(value)
}
return
}
//-------- Bitwise AND term
func newBitwiseAndTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseAnd,
evalFunc: evalBitwiseAnd,
}
}
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt & rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
return
}
//-------- Bitwise OR term
func newBitwiseOrTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseOr,
evalFunc: evalBitwiseOr,
}
}
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt | rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseOr(opTerm, leftValue, rightValue)
return
}
//-------- Bitwise XOR term
func newBitwiseXorTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseOr,
evalFunc: evalBitwiseXor,
}
}
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt ^ rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseXor(opTerm, leftValue, rightValue)
return
}
// init
func init() {
registerTermConstructor(SymTilde, newBitwiseNotTerm)
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
registerTermConstructor(SymCaret, newBitwiseXorTerm)
}
+1 -1
View File
@@ -20,7 +20,7 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
var sourceCtx ExprContext
if opTerm.children == nil || len(opTerm.children) == 0 {
if len(opTerm.children) == 0 {
sourceCtx = ctx
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
sourceCtx = globalCtx
+1 -1
View File
@@ -32,7 +32,7 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
opName := indexTerm.source()
if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, []any{})
v, err = unboxedValue.CallOperation(opName, map[string]any{})
} else {
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
v = false
+7 -3
View File
@@ -7,7 +7,6 @@ package expr
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
import (
"errors"
"fmt"
)
@@ -41,7 +40,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
if den == 0 {
err = errors.New("division by zero")
err = opTerm.errDivisionByZero()
return
}
@@ -49,6 +48,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
den = -den
num = -num
}
if num != 0 {
g := gcd(num, den)
num = num / g
den = den / g
@@ -57,10 +57,14 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
} else {
v = &FractionType{num, den}
}
} else {
v = &FractionType{0, den}
}
return
}
// init
func init() {
registerTermConstructor(SymVertBar, newFractionTerm)
// registerTermConstructor(SymVertBar, newFractionTerm)
registerTermConstructor(SymColon, newFractionTerm)
}
+3
View File
@@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
} else if IsDict(leftValue) {
d := leftValue.(*DictType)
v, err = getDictItem(d, indexTerm, indexList, rightValue)
} else {
rightChild := opTerm.children[1]
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
}
return
}
+8 -8
View File
@@ -4,15 +4,15 @@
// operator-insert.go
package expr
//-------- insert term
//-------- prepend term
func newInsertTerm(tk *Token) (inst *term) {
func newPrependTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priAssign,
evalFunc: evalInsert,
priority: priInsert,
evalFunc: evalPrepend,
}
}
@@ -21,12 +21,12 @@ func newAppendTerm(tk *Token) (inst *term) {
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priAssign,
priority: priInsert,
evalFunc: evalAppend,
}
}
func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
func evalPrepend(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
@@ -86,6 +86,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
// init
func init() {
registerTermConstructor(SymInsert, newInsertTerm)
registerTermConstructor(SymAppend, newAppendTerm)
registerTermConstructor(SymPlusGreater, newPrependTerm)
registerTermConstructor(SymLessPlus, newAppendTerm)
}
+3 -3
View File
@@ -11,7 +11,7 @@ func newIterValueTerm(tk *Token) (inst *term) {
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priIterValue,
priority: priDereference,
evalFunc: evalIterValue,
}
}
@@ -33,6 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
// init
func init() {
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymCaret, newIterValueTerm)
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymDereference, newIterValueTerm)
}
+93
View File
@@ -0,0 +1,93 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-post-inc-dec.go
package expr
// -------- post increment term
func newPostIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostInc,
}
}
func evalPostInc(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 {
var namePrefix string
v, err = it.Next()
if opTerm.children[0].symbol() == SymVariable {
namePrefix = opTerm.children[0].source()
}
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
if c, err1 := it.Current(); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_current", c)
}
if it.HasOperation(KeyName) {
if k, err1 := it.CallOperation(KeyName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_key", k)
}
}
if it.HasOperation(ValueName) {
if v1, err1 := it.CallOperation(ValueName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_value", v1)
}
}
} 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
}
// -------- post decrement term
func newPostDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostDec,
}
}
func evalPostDec(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
/* if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else */if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i-1)
} else {
err = opTerm.errIncompatibleType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm)
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
}
-41
View File
@@ -1,41 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-post-inc.go
package expr
// -------- post increment term
func newPostIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostInc,
}
}
func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i+1)
} else {
err = opTerm.errIncompatibleType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm)
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-pre-inc-dec.go
package expr
// -------- pre increment term
func newPreIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priIncDec,
evalFunc: evalPreInc,
}
}
func evalPreInc(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
i := childValue.(int64) + 1
ctx.SetVar(opTerm.children[0].source(), i)
v = i
} else {
err = opTerm.errIncompatibleType(childValue)
}
return
}
// -------- pre decrement term
func newPreDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priIncDec,
evalFunc: evalPreDec,
}
}
func evalPreDec(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
i := childValue.(int64) - 1
ctx.SetVar(opTerm.children[0].source(), i)
v = i
} else {
err = opTerm.errIncompatibleType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymPreInc, newPreIncTerm)
registerTermConstructor(SymPreDec, newPreDecTerm)
}
+65 -30
View File
@@ -5,7 +5,6 @@
package expr
import (
"errors"
"strings"
)
@@ -21,13 +20,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
}
}
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
return
}
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
if IsString(leftValue) && IsInteger(rightValue) {
s, _ := leftValue.(string)
n, _ := rightValue.(int64)
@@ -43,11 +36,21 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
v = leftInt * rightInt
}
} else {
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
return
}
return mulValues(prodTerm, leftValue, rightValue)
}
//-------- divide term
func newDivideTerm(tk *Token) (inst *term) {
@@ -60,18 +63,12 @@ func newDivideTerm(tk *Token) (inst *term) {
}
}
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) {
d := numAsFloat(rightValue)
if d == 0.0 {
err = errors.New("division by zero")
err = opTerm.errDivisionByZero()
} else {
v = numAsFloat(leftValue) / d
}
@@ -80,17 +77,52 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
} else {
leftInt, _ := leftValue.(int64)
if rightInt, _ := rightValue.(int64); rightInt == 0 {
err = errors.New("division by zero")
err = opTerm.errDivisionByZero()
} else {
v = leftInt / rightInt
}
}
} else if IsString(leftValue) && IsString(rightValue) {
source := leftValue.(string)
sep := rightValue.(string)
v = ListFromStrings(strings.Split(source, sep))
} else if IsString(leftValue) && IsInteger(rightValue) {
source := leftValue.(string)
partSize := int(rightValue.(int64))
if partSize == 0 {
err = opTerm.errDivisionByZero()
} else {
partCount := len(source) / partSize
remainder := len(source) % partSize
listSize := partCount
if remainder > 0 {
listSize++
}
parts := make([]any, 0, listSize)
for i := 0; i < partCount; i++ {
parts = append(parts, source[i*partSize:(i+1)*partSize])
}
if remainder > 0 {
parts = append(parts, source[len(source)-remainder:])
}
v = newList(parts)
}
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
return divValues(opTerm, leftValue, rightValue)
}
//-------- divide as float term
func newDivideAsFloatTerm(tk *Token) (inst *term) {
@@ -113,7 +145,7 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
d := numAsFloat(rightValue)
if d == 0.0 {
err = errors.New("division by zero")
err = floatDivTerm.errDivisionByZero()
} else {
v = numAsFloat(leftValue) / d
}
@@ -131,31 +163,34 @@ func newRemainderTerm(tk *Token) (inst *term) {
children: make([]*term, 0, 2),
position: posInfix,
priority: priProduct,
evalFunc: evalReminder,
evalFunc: evalRemainder,
}
}
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
return
}
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) {
rightInt, _ := rightValue.(int64)
if rightInt == 0 {
err = errors.New("division by zero")
err = opTerm.errDivisionByZero()
} else {
leftInt, _ := leftValue.(int64)
v = leftInt % rightInt
}
} else {
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
return
}
return remainderValues(remainderTerm, leftValue, rightValue)
}
// init
func init() {
registerTermConstructor(SymStar, newMultiplyTerm)
+18 -5
View File
@@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
}
}
func changeColonToRange(t *term) {
if t.tk.IsSymbol(SymColon) {
t.tk.Sym = SymRange
t.evalFunc = evalRange
}
}
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
// if err = self.checkOperands(); err != nil {
// return
// }
if len(opTerm.children) == 0 {
leftValue = int64(0)
rightValue = int64(-1)
@@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = errRangeInvalidSpecification(opTerm)
return
}
@@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
return
}
func errRangeInvalidSpecification(t *term) error {
return t.Errorf("invalid range specification")
}
func errRangeUnexpectedExpression(t *term) error {
return t.Errorf("unexpected range expression")
}
// init
func init() {
registerTermConstructor(SymColon, newRangeTerm)
registerTermConstructor(SymRange, newRangeTerm)
}
+11 -1
View File
@@ -22,9 +22,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
if len(filterList) == 0 && exprValue == int64(caseIndex) {
if len(filterList) == 0 {
var valueAsInt = int64(0)
if b, ok := exprValue.(bool); ok {
if !b {
valueAsInt = 1
}
} else if valueAsInt, ok = exprValue.(int64); !ok {
return
}
if valueAsInt == int64(caseIndex) {
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
}
} else {
var caseValue any
for _, caseTerm := range filterList {
+77
View File
@@ -0,0 +1,77 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-shift.go
package expr
//-------- bit right shift term
func newRightShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalRightShift,
}
}
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt >> rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitRightShift(opTerm, leftValue, rightValue)
return
}
func newLeftShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalLeftShift,
}
}
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt << rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitLeftShift(opTerm, leftValue, rightValue)
return
}
// init
func init() {
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
}
+24 -14
View File
@@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
}
}
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
return
}
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
v = fmt.Sprintf("%v%v", leftValue, rightValue)
} else if IsNumber(leftValue) && IsNumber(rightValue) {
@@ -59,10 +53,22 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
c := leftDict.clone()
c.merge(rightDict)
v = c
} else if isFraction(leftValue) && isFraction(rightValue) {
v, err = sumAnyFract(leftValue, rightValue)
} else {
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
}
return v, err
}
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
return
}
return sumValues(plusTerm, leftValue, rightValue)
}
//-------- minus term
@@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
}
}
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
return
}
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) - numAsFloat(rightValue)
@@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
return
}
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
return
}
return diffValues(minusTerm, leftValue, rightValue)
}
// init
func init() {
registerTermConstructor(SymPlus, newPlusTerm)
+177 -85
View File
@@ -6,10 +6,46 @@ package expr
import (
"errors"
"golang.org/x/exp/constraints"
)
//-------- parser
type parserContext uint16
const (
parserNoFlags = 0
allowMultiExpr parserContext = 1 << iota
allowVarRef
selectorContext
listContext // squareContext for list
indexContext // squareContext for index
allowIndex // allow index in squareContext
squareContext = listContext | indexContext // Square parenthesis for list or index
)
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
return (set & singleFlag) != 0
}
func addFlags[T constraints.Unsigned](set T, flags T) T {
return set | flags
}
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
if cond {
newSet = set | flags
} else {
newSet = set
}
return
}
func remFlags[T constraints.Unsigned](set T, flags T) T {
return set & (^flags)
}
type parser struct {
}
@@ -19,17 +55,18 @@ func NewParser() (p *parser) {
}
func (parser *parser) Next(scanner *scanner) (tk *Token) {
for tk=scanner.Next(); tk.IsSymbol(SymComment); tk=scanner.Next() {}
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) {
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10)
itemExpected := false
lastSym := SymUnknown
for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
break
}
prev := scanner.Previous()
@@ -65,12 +102,18 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) {
param := newTerm(tk)
if len(args) > 0 {
if pos := paramAlreadyDefined(args, param); pos > 0 {
err = tk.Errorf("parameter %q at position %d already defined at position %d", param.source(), len(args)+1, pos)
break
}
}
args = append(args, param)
tk = parser.Next(scanner)
if tk.Sym == SymEqual {
var paramExpr *ast
defaultParamsStarted = true
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
break
}
param.forceChild(paramExpr.root)
@@ -93,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
if err == nil {
tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) {
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
} else {
err = tk.ErrorExpectedGot("{")
}
@@ -109,27 +152,43 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
return
}
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
func paramAlreadyDefined(args []*term, param *term) (position int) {
position = 0
for i, arg := range args {
if arg.source() == param.source() {
position = i + 1
}
}
return
}
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
r, c := scanner.lastPos()
args := make([]*term, 0)
lastSym := SymUnknown
itemExpected := false
itemCtx := remFlags(ctx, allowIndex)
for lastSym != SymClosedSquare && lastSym != SymEos {
var subTree *ast
zeroRequired := scanner.current.Sym == SymColon
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
root := subTree.root
var itemTree *ast
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
root := itemTree.root
if root != nil {
if !parsingIndeces && root.symbol() == SymColon {
err = root.Errorf("unexpected range expression")
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
changeColonToRange(root)
}
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
// err = root.Errorf("unexpected range expression")
err = errRangeUnexpectedExpression(root)
break
}
args = append(args, root)
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
if len(root.children) == 1 {
root.children = append(root.children, root.children[0])
} else if len(root.children) > 1 {
err = root.Errorf("invalid range specification")
// err = root.Errorf("invalid range specification")
err = errRangeInvalidSpecification(root)
break
}
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
@@ -146,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
break
}
lastSym = scanner.Previous().Sym
itemExpected = lastSym == SymComma
if itemExpected = lastSym == SymComma; itemExpected {
remFlags(ctx, allowIndex)
}
}
if err == nil {
if lastSym != SymClosedSquare {
err = scanner.Previous().ErrorExpectedGot("]")
} else {
subtree = newListTerm(r, c, args)
listTerm = newListTerm(r, c, args)
}
}
return
}
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
tk := scanner.Previous()
args := make([]*term, 0)
lastSym := SymUnknown
itemExpected := false
for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
if subTree.root != nil {
args = append(args, subTree.root)
} else if itemExpected {
@@ -211,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
return
}
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
args := make(map[any]*term, 0)
lastSym := SymUnknown
itemExpected := false
@@ -228,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
}
break
}
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
if subTree.root != nil {
args[key] = subTree.root
} else /*if key != nil*/ {
@@ -247,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
err = scanner.Previous().ErrorExpectedGot("}")
} else {
subtree = newDictTerm(args)
// subtree = newMapTerm(args)
}
}
return
}
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
var filterList *term
var caseExpr *ast
ctx = remFlags(ctx, allowIndex)
tk := parser.Next(scanner)
startRow := tk.row
startCol := tk.col
@@ -264,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
err = tk.Errorf("case list in default clause")
return
}
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
return
}
tk = parser.Next(scanner)
@@ -275,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
}
if tk.Sym == SymOpenBrace {
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
return
}
} else {
@@ -301,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
caseTerm.parent = selectorTerm
}
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
var caseTerm *term
ctx = remFlags(ctx, allowIndex)
tk := scanner.makeToken(SymSelector, '?')
if selectorTerm, err = tree.addToken2(tk); err != nil {
if selectorTerm, err = tree.addToken(tk); err != nil {
return
}
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
addSelectorCase(selectorTerm, caseTerm)
}
return
}
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
}
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
return parser.parseGeneral(scanner, true, false, termSymbols...)
termSymbols = append(termSymbols, SymEos)
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
}
func couldBeACollection(t *term) bool {
@@ -330,28 +394,53 @@ func couldBeACollection(t *term) bool {
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
}
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
// var areOut = false
// if ctxTerm != nil {
// areOut = tk.IsOneOf(syms)
// }
// return areOut
// }
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
var tk *Token
if allowIndeces {
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
root = newTerm(tk)
if err = tree.addTerm(root); err == nil {
err = tree.addTerm(listTerm)
}
} else {
root = listTerm
err = tree.addTerm(listTerm)
}
return
}
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
func changePrefix(tk *Token) {
switch tk.Sym {
case SymMinus:
tk.SetSymbol(SymChangeSign)
case SymPlus:
tk.SetSymbol(SymUnchangeSign)
case SymStar:
tk.SetSymbol(SymDereference)
case SymExclamation:
tk.SetSymbol(SymNot)
case SymDoublePlus:
tk.SetSymbol(SymPreInc)
case SymDoubleMinus:
tk.SetSymbol(SymPreDec)
}
}
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
var selectorTerm *term = nil
var currentTerm *term = nil
var tk *Token
tree = NewAst()
firstToken := true
// lastSym := SymUnknown
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
if tk.Sym == SymComment {
continue
}
// if tk.Sym == SymComment {
// continue
// }
if tk.Sym == SymSemiColon {
if allowForest {
if hasFlag(ctx, allowMultiExpr) {
tree.ToForest()
firstToken = true
currentTerm = nil
@@ -365,57 +454,55 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
//fmt.Println("Token:", tk)
if firstToken {
if tk.Sym == SymMinus {
tk.Sym = SymChangeSign
} else if tk.Sym == SymPlus {
tk.Sym = SymUnchangeSign
}
changePrefix(tk)
// if tk.Sym == SymMinus {
// tk.Sym = SymChangeSign
// } else if tk.Sym == SymPlus {
// tk.Sym = SymUnchangeSign
// } else if tk.IsSymbol(SymStar) {
// tk.SetSymbol(SymDereference)
// } else if tk.IsSymbol(SymExclamation) {
// tk.SetSymbol(SymNot)
// }
firstToken = false
}
switch tk.Sym {
case SymOpenRound:
var subTree *ast
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
subTree.root.priority = priValue
err = tree.addTerm(newExprTerm(subTree.root))
currentTerm = subTree.root
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
exprTerm := newExprTerm(subTree.root)
err = tree.addTerm(exprTerm)
currentTerm = exprTerm
// subTree.root.priority = priValue
// err = tree.addTerm(newExprTerm(subTree.root))
// currentTerm = subTree.root
}
case SymFuncCall:
var funcCallTerm *term
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
err = tree.addTerm(funcCallTerm)
currentTerm = funcCallTerm
}
case SymOpenSquare:
var listTerm *term
parsingIndeces := couldBeACollection(currentTerm)
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
if parsingIndeces {
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
indexTerm := newTerm(indexTk)
if err = tree.addTerm(indexTerm); err == nil {
err = tree.addTerm(listTerm)
}
} else {
err = tree.addTerm(listTerm)
}
currentTerm = listTerm
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
}
case SymOpenBrace:
if currentTerm != nil && currentTerm.symbol() == SymColon {
err = currentTerm.Errorf(`selector-case outside of a selector context`)
} else {
var mapTerm *term
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
err = tree.addTerm(mapTerm)
currentTerm = mapTerm
}
}
case SymEqual:
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
currentTerm, err = tree.addToken2(tk)
// }
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
currentTerm, err = tree.addToken(tk)
firstToken = true
case SymFuncDef:
var funcDefTerm *term
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
@@ -424,24 +511,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
}
case SymDollarRound:
var iterDefTerm *term
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
err = tree.addTerm(iterDefTerm)
currentTerm = iterDefTerm
}
case SymIdentifier:
if tk.source[0] == '@' && !allowVarRef {
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
} else {
currentTerm, err = tree.addToken2(tk)
currentTerm, err = tree.addToken(tk)
}
case SymQuestion:
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
currentTerm = selectorTerm
addFlags(ctx, selectorContext)
}
case SymColon, SymDoubleColon:
var caseTerm *term
if selectorTerm != nil {
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
addSelectorCase(selectorTerm, caseTerm)
currentTerm = caseTerm
if tk.Sym == SymDoubleColon {
@@ -449,31 +537,35 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
}
}
} else {
currentTerm, err = tree.addToken2(tk)
}
if tk.IsSymbol(SymColon) {
currentTerm, err = tree.addToken(tk)
if tk.IsOneOfA(SymColon, SymRange) {
// Colon outside a selector term acts like a separator
firstToken = true
}
}
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 {
selectorTerm = nil
remFlags(ctx, selectorContext)
}
// lastSym = tk.Sym
}
if err == nil {
if !tk.IsOneOf(termSymbols) {
var symDesc string
if tk.IsSymbol(SymError) {
symDesc = tk.ErrorText()
} else {
symDesc = SymToString(tk.Sym)
}
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
} else {
err = tk.Error()
}
}
return
}
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
// if lastSym != wantedSym {
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
// }
// return
// }
+67 -10
View File
@@ -16,6 +16,7 @@ import (
type scanner struct {
current *Token
prev *Token
stage *Token
stream *bufio.Reader
row int
column int
@@ -39,7 +40,7 @@ func DefaultTranslations() map[Symbol]Symbol {
SymKwAnd: SymAnd,
SymDoubleVertBar: SymOr,
SymKwOr: SymOr,
SymTilde: SymNot,
// SymTilde: SymNot,
SymKwNot: SymNot,
SymLessGreater: SymNotEqual,
}
@@ -74,6 +75,16 @@ func (scanner *scanner) unreadChar() (err error) {
return
}
func (scanner *scanner) UnreadToken() (err error) {
if scanner.stage == nil {
scanner.stage = scanner.current
scanner.current = scanner.prev
} else {
err = fmt.Errorf("staging already present, currently one level only of staging is allowed")
}
return
}
func (scanner *scanner) lastPos() (r, c int) {
if scanner.prev != nil {
r = scanner.prev.row
@@ -89,7 +100,12 @@ func (scanner *scanner) Previous() *Token {
func (scanner *scanner) Next() (tk *Token) {
scanner.prev = scanner.current
tk = scanner.current
if scanner.stage != nil {
scanner.current = scanner.stage
scanner.stage = nil
} else {
scanner.current = scanner.fetchNextToken()
}
return tk
}
@@ -108,6 +124,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.moveOn(SymDoublePlus, ch, next)
} else if next == '=' {
tk = scanner.moveOn(SymPlusEqual, ch, next)
} else if next == '>' {
tk = scanner.moveOn(SymPlusGreater, ch, next)
} else {
tk = scanner.makeToken(SymPlus, ch)
}
@@ -124,6 +142,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.moveOn(SymDoubleStar, ch, next)
// } else if next == '/' {
// tk = self.moveOn(SymClosedComment, ch, next)
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymStarEqual, ch, next)
} else {
tk = scanner.makeToken(SymStar, ch)
}
@@ -131,6 +151,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
if next, _ := scanner.peek(); next == '*' {
scanner.readChar()
tk = scanner.fetchBlockComment()
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymSlashEqual, ch, next)
} else if next == '/' {
scanner.readChar()
tk = scanner.fetchOnLineComment()
@@ -147,13 +169,19 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
case '|':
if next, _ := scanner.peek(); next == '|' {
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymVertBarEqual, ch, next)
} else {
tk = scanner.makeToken(SymVertBar, ch)
}
case ',':
tk = scanner.makeToken(SymComma, ch)
case '^':
if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymCaretEqual, ch, next)
} else {
tk = scanner.makeToken(SymCaret, ch)
}
case ':':
if next, _ := scanner.peek(); next == ':' {
tk = scanner.moveOn(SymDoubleColon, ch, next)
@@ -212,11 +240,17 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
case '&':
if next, _ := scanner.peek(); next == '&' {
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
} else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymAmpersandEqual, ch, next)
} else {
tk = scanner.makeToken(SymAmpersand, ch)
}
case '%':
if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymPercEqual, ch, next)
} else {
tk = scanner.makeToken(SymPercent, ch)
}
case '#':
tk = scanner.makeToken(SymHash, ch)
case '@':
@@ -245,9 +279,18 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymLessOrEqual, ch, next)
} else if next == '<' {
tk = scanner.moveOn(SymAppend, ch, next)
scanner.readChar()
next2, _ := scanner.readChar()
scanner.unreadChar()
if next2 == '=' {
tk = scanner.moveOn(SymDoubleLessEqual, ch, next, next2)
} else {
tk = scanner.accept(SymDoubleLess, ch, next)
}
} else if next == '>' {
tk = scanner.moveOn(SymLessGreater, ch, next)
} else if next == '+' {
tk = scanner.moveOn(SymLessPlus, ch, next)
} else {
tk = scanner.makeToken(SymLess, ch)
}
@@ -255,7 +298,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
} else if next == '>' {
tk = scanner.moveOn(SymInsert, ch, next)
scanner.readChar()
next2, _ := scanner.readChar()
scanner.unreadChar()
if next2 == '=' {
tk = scanner.moveOn(SymDoubleGreaterEqual, ch, next, next2)
} else {
tk = scanner.accept(SymDoubleGreater, ch, next)
}
} else {
tk = scanner.makeToken(SymGreater, ch)
}
@@ -269,11 +319,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.makeToken(SymDollar, ch)
}
case '(':
// if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else {
// if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else {
tk = scanner.makeToken(SymOpenRound, ch)
// }
// }
case ')':
tk = scanner.makeToken(SymClosedRound, ch)
case '[':
@@ -440,7 +490,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
tk = scanner.makeErrorToken(err)
} else {
var value any
err = scanner.sync(err) // TODO: Check this function
_ = scanner.sync(err) // TODO: Check this function
txt := sb.String()
if sym == SymFloat {
value, err = strconv.ParseFloat(txt, 64)
@@ -572,7 +622,7 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
}
if err != nil {
if err == io.EOF {
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
tk = scanner.makeErrorToken(errors.New(string(termCh)))
} else {
tk = scanner.makeErrorToken(err)
}
@@ -610,9 +660,16 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
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++ {
if len(chars) > 1 {
scanner.readChar()
}
// }
return
}
func (scanner *scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return
}
+9 -3
View File
@@ -24,6 +24,11 @@ func NewSimpleStore() *SimpleStore {
return ctx
}
func (ss *SimpleStore) Init() {
ss.varStore = make(map[string]any)
ss.funcStore = make(map[string]ExprFunc)
}
func filterRefName(name string) bool { return name[0] != '@' }
//func filterPrivName(name string) bool { return name[0] != '_' }
@@ -149,10 +154,11 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
}
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (exprFunc ExprFunc, err error) {
var info *funcInfo
if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
ctx.funcStore[name] = info
exprFunc = info
}
return
}
@@ -179,10 +185,10 @@ func (ctx *SimpleStore) DeleteFunc(funcName string) {
delete(ctx.funcStore, funcName)
}
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
func (ctx *SimpleStore) Call(name string, args map[string]any) (result any, err error) {
if info, exists := GetLocalFuncInfo(ctx, name); exists {
functor := info.Functor()
result, err = functor.Invoke(ctx, name, args)
result, err = functor.InvokeNamed(ctx, name, args)
} else {
err = fmt.Errorf("unknown function %s()", name)
}
+198
View File
@@ -0,0 +1,198 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// Symbol.go
package expr
import (
"strings"
)
var symbolMap map[Symbol]symbolSpec
type symbolClass int16
const (
symClassOperator symbolClass = iota
symClassCommand
symClassIdentifier
symClassDelimiter
symClassParenthesis
symClassDeclaration
symClassValue
symClassOther
)
type symbolSpec struct {
repr string
kind symbolClass
opType termPosition
}
func init() {
symbolMap = map[Symbol]symbolSpec{
SymUnknown: {"<unknown>", symClassOther, posLeaf}, // -1: Unknown symbol
SymNone: {"<null>", symClassOther, posLeaf}, // 0: Null value for variable of type symbol
SymError: {"<error>", symClassOther, posLeaf}, // 1: Error reading from stream
SymEos: {"<eos>", symClassOther, posLeaf}, // 2: End of stream
SymMinus: {"-", symClassOperator, posInfix}, // 3: '-'
SymMinusEqual: {"-=", symClassOperator, posInfix}, // 4: '-='
SymDoubleMinus: {"--", symClassOperator, posPostfix}, // 5: '--'
SymPlus: {"+", symClassOperator, posInfix}, // 6: '+'
SymPlusEqual: {"+=", symClassOperator, posInfix}, // 7: '+='
SymDoublePlus: {"++", symClassOperator, posPostfix}, // 8: '++'
SymStar: {"*", symClassOperator, posInfix}, // 9: '*'
SymDoubleStar: {"**", symClassOperator, posInfix}, // 10: '**'
SymSlash: {"/", symClassOperator, posInfix}, // 11: '/'
SymBackSlash: {"\\", symClassOperator, posLeaf}, // 12: '\'
SymVertBar: {"|", symClassOperator, posInfix}, // 13: '|'
SymDoubleVertBar: {"||", symClassOperator, posInfix}, // 14: '||'
SymComma: {",", symClassOperator, posInfix}, // 15: ','
SymColon: {":", symClassOperator, posInfix}, // 16: ':'
SymSemiColon: {";", symClassOperator, posInfix}, // 17: ';'
SymDot: {".", symClassOperator, posInfix}, // 18: '.'
SymDotSlash: {"./", symClassOperator, posInfix}, // 19: './'
SymQuote: {"'", symClassDelimiter, posLeaf}, // 20: '\''
SymDoubleQuote: {"\"", symClassDelimiter, posLeaf}, // 21: '"'
SymBackTick: {"`", symClassDelimiter, posLeaf}, // 22: '`'
SymExclamation: {"!", symClassOperator, posPostfix}, // 23: '!'
SymQuestion: {"?", symClassOperator, posInfix}, // 24: '?'
SymAmpersand: {"&", symClassOperator, posInfix}, // 25: '&'
SymDoubleAmpersand: {"&&", symClassOperator, posInfix}, // 26: '&&'
SymPercent: {"%", symClassOperator, posInfix}, // 27: '%'
SymAt: {"@", symClassOperator, posPrefix}, // 28: '@'
SymUndescore: {"_", symClassIdentifier, posLeaf}, // 29: '_'
SymEqual: {"=", symClassOperator, posInfix}, // 30: '='
SymDoubleEqual: {"==", symClassOperator, posInfix}, // 31: '=='
SymLess: {"<", symClassOperator, posInfix}, // 32: '<'
SymLessOrEqual: {"<=", symClassOperator, posInfix}, // 33: '<='
SymGreater: {">", symClassOperator, posInfix}, // 34: '>'
SymGreaterOrEqual: {">=", symClassOperator, posInfix}, // 35: '>='
SymLessGreater: {"<>", symClassOperator, posInfix}, // 36: '<>'
SymNotEqual: {"!=", symClassOperator, posInfix}, // 37: '!='
SymDollar: {"$", symClassOperator, posPrefix}, // 38: '$'
SymHash: {"#", symClassOperator, posPrefix}, // 39: '#'
SymOpenRound: {"(", symClassParenthesis, posPrefix}, // 40: '('
SymClosedRound: {")", symClassParenthesis, posPostfix}, // 41: ')'
SymOpenSquare: {"[", symClassParenthesis, posPrefix}, // 42: '['
SymClosedSquare: {"]", symClassParenthesis, posPostfix}, // 43: ']'
SymOpenBrace: {"{", symClassParenthesis, posPrefix}, // 44: '{'
SymClosedBrace: {"}", symClassParenthesis, posPostfix}, // 45: '}'
SymTilde: {"~", symClassOperator, posPrefix}, // 46: '~'
SymDoubleQuestion: {"??", symClassOperator, posInfix}, // 47: '??'
SymQuestionEqual: {"?=", symClassOperator, posInfix}, // 48: '?='
SymQuestionExclam: {"?!", symClassOperator, posInfix}, // 49: '?!'
SymDoubleAt: {"@@", symClassCommand, posLeaf}, // 50: '@@'
SymDoubleColon: {"::", symClassOperator, posInfix}, // 51: '::'
SymDoubleGreater: {">>", symClassOperator, posInfix}, // 52: '>>'
SymDoubleLess: {"<<", symClassOperator, posInfix}, // 53: '<<'
SymCaret: {"^", symClassOperator, posInfix}, // 54: '^'
SymDollarRound: {"$(", symClassOperator, posPrefix}, // 55: '$('
SymOpenClosedRound: {"()", symClassOperator, posPostfix}, // 56: '()'
SymDoubleDollar: {"$$", symClassCommand, posLeaf}, // 57: '$$'
SymDoubleDot: {"..", symClassOperator, posInfix}, // 58: '..'
SymTripleDot: {"...", symClassOperator, posPostfix}, // 59: '...'
SymStarEqual: {"*=", symClassOperator, posInfix}, // 60: '*='
SymSlashEqual: {"/=", symClassOperator, posInfix}, // 61: '/='
SymPercEqual: {"%=", symClassOperator, posInfix}, // 62: '%='
SymDoubleLessEqual: {"<<=", symClassOperator, posInfix}, // 63: '<<='
SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix}, // 64: '>>='
SymAmpersandEqual: {"&=", symClassOperator, posInfix}, // 65: '&='
SymVertBarEqual: {"|=", symClassOperator, posInfix}, // 65: '|='
SymCaretEqual: {"^=", symClassOperator, posInfix}, // 66: '^='
SymPlusGreater: {"+>", symClassOperator, posInfix}, // 67: '+>'
SymLessPlus: {"<+", symClassOperator, posInfix}, // 68: '<+'
SymPreInc: {"++", symClassOperator, posPrefix}, // : '++'
SymPreDec: {"--", symClassOperator, posPrefix}, // : '--'
// SymChangeSign
// SymUnchangeSign
// SymIdentifier
// SymBool
// SymInteger
// SymVariable
// SymFloat
// SymFraction
// SymString
// SymIterator
// SymOr: "or",
// SymAnd: "and",
// SymNot: "not",
// SymComment
// SymFuncCall
// SymFuncDef
// SymList
// SymDict
// SymIndex
// SymExpression
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
// // SymOpenComment // 0: '/*'
// // SymClosedComment // 0: '*/'
// // SymOneLineComment // 0: '//'
// keywordBase
SymKwAnd: {"and", symClassOperator, posInfix},
SymKwNot: {"not", symClassOperator, posInfix},
SymKwOr: {"or", symClassOperator, posInfix},
SymKwBut: {"but", symClassOperator, posInfix},
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
SymKwPlugin: {"plugin", symClassOperator, posPrefix},
SymKwIn: {"in", symClassOperator, posInfix},
SymKwInclude: {"include", symClassOperator, posPrefix},
SymKwNil: {"nil", symClassValue, posLeaf},
SymKwUnset: {"unset", symClassOperator, posPrefix},
}
}
func SymToString(sym Symbol) string {
if s, ok := symbolMap[sym]; ok {
return s.repr
}
return ""
}
func SymListToString(symList []Symbol, quote bool) string {
var sb strings.Builder
if len(symList) == 0 {
sb.WriteString("<nothing>")
} else {
for _, sym := range symList {
if sb.Len() > 0 {
sb.WriteByte(',')
sb.WriteByte(' ')
}
if quote {
sb.WriteByte('`')
}
sb.WriteString(SymToString(sym))
if quote {
sb.WriteByte('`')
}
}
}
return sb.String()
}
func StringEndsWithOperator(s string) bool {
return endingOperator(s) != SymNone
}
func endingOperator(s string) (sym Symbol) {
var matchLength = 0
sym = SymNone
lower := strings.TrimRight(strings.ToLower(s), " \t")
for symbol, spec := range symbolMap {
if strings.HasSuffix(lower, spec.repr) {
if len(spec.repr) > matchLength {
matchLength = len(spec.repr)
if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
sym = symbol
} else {
sym = SymNone
}
}
}
}
return
}
+16 -2
View File
@@ -60,16 +60,29 @@ const (
SymQuestionExclam // 49: '?!'
SymDoubleAt // 50: '@@'
SymDoubleColon // 51: '::'
SymInsert // 52: '>>'
SymAppend // 53: '<<'
SymDoubleGreater // 52: '>>'
SymDoubleLess // 53: '<<'
SymCaret // 54: '^'
SymDollarRound // 55: '$('
SymOpenClosedRound // 56: '()'
SymDoubleDollar // 57: '$$'
SymDoubleDot // 58: '..'
SymTripleDot // 59: '...'
SymStarEqual // 60: '*='
SymSlashEqual // 61: '/='
SymPercEqual // 62: '%='
SymDoubleLessEqual // 63: '<<='
SymDoubleGreaterEqual // 64: '>>='
SymAmpersandEqual // 65: '&='
SymVertBarEqual // 65: '|='
SymCaretEqual // 66: '^='
SymPlusGreater // 67: '+>'
SymLessPlus // 68: '<+'
SymChangeSign
SymUnchangeSign
SymDereference
SymPreInc
SymPreDec
SymIdentifier
SymBool
SymInteger
@@ -87,6 +100,7 @@ const (
SymList
SymDict
SymIndex
SymRange // [index : index]
SymExpression
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
+1 -1
View File
@@ -47,7 +47,7 @@ func TestAddUnknownTokens(t *testing.T) {
wantErr := errors.New(`unexpected token "%"`)
tree := NewAst()
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
if _, gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr)
}
}
+2 -1
View File
@@ -26,12 +26,13 @@ func TestBool(t *testing.T) {
/* 12 */ {`true or false`, true, nil},
/* 13 */ {`true or []`, true, nil},
/* 14 */ {`[] or false`, nil, errors.New(`got list as left operand type of 'OR' operator, it must be bool`)},
/* 15 */ {`!true`, false, nil},
/* 13 */ //{`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1)
// runTestSuiteSpec(t, section, inputs, 15)
runTestSuite(t, section, inputs)
}
+19 -12
View File
@@ -21,7 +21,7 @@ func TestFuncBase(t *testing.T) {
/* 7 */ {`int(3.9)`, int64(3), nil},
/* 8 */ {`int("432")`, int64(432), nil},
/* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
/* 10 */ {`int("432", 4)`, nil, `int(): too much params -- expected 1, got 2`},
/* 10 */ {`int("432", 4)`, nil, `int(): too many params -- expected 1, got 2`},
/* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
/* 12 */ {`isInt(2+1)`, true, nil},
/* 13 */ {`isInt(3.1)`, false, nil},
@@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
/* 16 */ {`isString("3" + 1)`, true, nil},
/* 17 */ {`isList(["3", 1])`, true, nil},
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
/* 19 */ {`isFract(1|3)`, true, nil},
/* 20 */ {`isFract(3|1)`, false, nil},
/* 21 */ {`isRational(3|1)`, true, nil},
/* 19 */ {`isFract(1:3)`, true, nil},
/* 20 */ {`isFract(3:1)`, false, nil},
/* 21 */ {`isRational(3:1)`, true, nil},
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
@@ -41,25 +41,32 @@ func TestFuncBase(t *testing.T) {
/* 27 */ {`dec(2.0)`, float64(2), nil},
/* 28 */ {`dec("2.0")`, float64(2), nil},
/* 29 */ {`dec(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too much params -- expected 1, got 3`},
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
/* 33 */ {`isBool(false)`, true, nil},
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
/* 34 */ {`fract(1:2)`, newFraction(1, 2), nil},
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
/* 36 */ {`bool(2)`, true, nil},
/* 37 */ {`bool(1|2)`, true, nil},
/* 37 */ {`bool(1:2)`, true, nil},
/* 38 */ {`bool(1.0)`, true, nil},
/* 39 */ {`bool("1")`, true, nil},
/* 40 */ {`bool(false)`, false, nil},
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
/* 42 */ {`dec(false)`, float64(0), nil},
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
/* 41 */ {`bool([1])`, true, nil},
/* 42 */ {`bool([])`, false, nil},
/* 43 */ {`bool({})`, false, nil},
/* 44 */ {`bool({1:"one"})`, true, nil},
/* 45 */ {`dec(false)`, float64(0), nil},
/* 46 */ {`dec(1:2)`, float64(0.5), nil},
/* 47 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
/* 48 */ {`eval("a=3"); a`, int64(3), nil},
/* 49 */ {`int(5:2)`, int64(2), nil},
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
}
t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 49)
runTestSuite(t, section, inputs)
}
+1 -1
View File
@@ -30,7 +30,7 @@ func TestFmt(t *testing.T) {
text := "ciao mondo"
inputs := []inputType{
/* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
/* 1 */ {fmt.Sprintf(`builtin "fmt"; println("%s")`, text), int64(11), nil},
}
// t.Setenv("EXPR_PATH", ".")
+1 -1
View File
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
t.Setenv("EXPR_PATH", "test-resources")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}
+4 -2
View File
@@ -17,12 +17,14 @@ func TestFuncRun(t *testing.T) {
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, `paramter "operator" must be a function, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil},
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
}
//t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
//runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs)
}
+6 -1
View File
@@ -19,10 +19,15 @@ func TestFuncMathArith(t *testing.T) {
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
/* 7 */ {`builtin "math.arith"; a=5; b=2; add(a, b*3)`, int64(11), nil},
/* 8 */ {`builtin "math.arith"; var2="abc"; add(1,2) but var2`, "abc", nil},
/* 9 */ {`builtin "math.arith"; add()`, int64(0), nil},
/* 10 */ {`builtin "math.arith"; mul()`, int64(1), nil},
/* 11 */ {`builtin "math.arith"; add([1,2,3])`, int64(6), nil},
/* 12 */ {`builtin "math.arith"; mul([2,2,3])`, int64(12), nil},
/* 13 */ {`builtin "math.arith"; mul(2,2,3)`, int64(12), nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs)
}
+1 -1
View File
@@ -30,6 +30,6 @@ func TestFuncOs(t *testing.T) {
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1)
//runTestSuiteSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs)
}
+3 -1
View File
@@ -15,7 +15,7 @@ func TestFuncString(t *testing.T) {
/* 1 */ {`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},
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `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 an integer (1)`},
/* 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},
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
@@ -31,6 +31,8 @@ func TestFuncString(t *testing.T) {
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
/* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil},
/* 21 */ {`builtin "string"; strLower("StOp")`, "stop", nil},
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
}
+5 -3
View File
@@ -42,6 +42,8 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
failed := 0
for i, input := range inputs {
// fmt.Printf("%3d: %s\n", i+1, input.source)
good := doTest(t, ctx, section, &input, i+1)
if good {
succeeded++
@@ -91,7 +93,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ {
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
good = false
}
@@ -106,8 +108,8 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
if wantErr == nil {
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
t.Logf("[+]%s nr %3d -- `%s` --> %v", section, n, source, wantResult)
} else {
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
}
}
+16 -2
View File
@@ -17,7 +17,21 @@ func TestExpr(t *testing.T) {
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {`
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
/* 8 */ {`a=3; a*=2; a`, 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`, int64(1), nil},
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
/* 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 */ {`true ? {"a"} :: {"b"}`, "a", nil},
/* 18 */ {`$$`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
/* 19 */ {`
ds={
"init":func(@end){@current=0 but true},
//"current":func(){current},
@@ -32,6 +46,6 @@ func TestExpr(t *testing.T) {
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 6)
// runTestSuiteSpec(t, section, inputs, 18)
runTestSuite(t, section, inputs)
}
+23 -20
View File
@@ -11,35 +11,39 @@ import (
func TestFractionsParser(t *testing.T) {
section := "Fraction"
inputs := []inputType{
/* 1 */ {`1|2`, newFraction(1, 2), nil},
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
/* 10 */ {`1|(-2)`, newFraction(-1, 2), 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},
/* 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},
/* 15 */ {`1|0`, nil, `division by zero`},
/* 1 */ {`1:2`, newFraction(1, 2), nil},
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
/* 10 */ {`1:(-2)`, newFraction(-1, 2), 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},
/* 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},
/* 15 */ {`1:0`, nil, `[1:3] division by zero`},
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
/* 22 */ {`string(1|2)`, "1|2", nil},
/* 22 */ {`string(1:2)`, "1:2", nil},
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
/* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
}
// runTestSuiteSpec(t, section, inputs, 25)
runTestSuite(t, section, inputs)
}
func TestFractionToStringSimple(t *testing.T) {
source := newFraction(1, 2)
want := "1|2"
want := "1:2"
got := source.ToString(0)
if got != want {
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
@@ -55,10 +59,9 @@ func TestFractionToStringMultiline(t *testing.T) {
}
}
// TODO Check this test: the output string ends with a space
func _TestToStringMultilineTty(t *testing.T) {
func TestToStringMultilineTty(t *testing.T) {
source := newFraction(-1, 2)
want := "\x1b[4m-1\x1b[0m\n2"
want := "\x1b[4m-1\x1b[0m\n 2"
got := source.ToString(MultiLine | TTY)
if got != want {
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
+35 -6
View File
@@ -24,24 +24,30 @@ func TestFuncs(t *testing.T) {
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too many params -- expected 0, got 1`},
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
/* 20 */ {`f=func(a,b){a*2+b}; f(1,10)`, int64(12), nil},
/* 21 */ {`f=func(a,b){a*2+b}; f(a=2,b=1)`, int64(5), nil},
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
/* 25 */ {`f=func(x,x){x}`, nil, `[1:11] parameter "x" at position 2 already defined at position 1`},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 20)
//runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs)
}
func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
func dummy(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return
}
@@ -54,7 +60,6 @@ func TestFunctionToStringSimple(t *testing.T) {
}
}
func TestFunctionGetFunc(t *testing.T) {
source := NewGolangFunctor(dummy)
want := ExprFunc(nil)
@@ -63,3 +68,27 @@ func TestFunctionGetFunc(t *testing.T) {
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
}
}
func TestGoFunction(t *testing.T) {
section := "Funcs"
inputs := []inputType{
/* 1 */ {`myName()`, "Celestino Amoroso", nil},
/* 2 */ {`myName("Peppino")`, "Peppino", nil},
}
myName := func(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var ok bool
if result, ok = args["name"].(string); !ok {
err = ErrWrongParamType(name, "name", TypeString, args["name"])
}
return
}
ctx := NewSimpleStore()
ctx.RegisterFunc("myName", NewGolangFunctor(myName), TypeString, []ExprFuncParam{
NewFuncParamFlagDef("name", PfOptional|PfDefault, "Celestino Amoroso"),
})
runCtxTestSuite(t, ctx, section, inputs)
}
+40 -39
View File
@@ -5,50 +5,51 @@
package expr
import (
"fmt"
"testing"
)
func subtract(ctx ExprContext, name string, args []any) (result any, err error) {
if len(args) != 2 {
err = fmt.Errorf("%s(): requires exactly two arguments", name)
return
}
x, xok := args[0].(int64)
y, yok := args[1].(int64)
if xok && yok {
result = x - y
} else {
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
}
return
}
// TODO The new function param model does not allow this kind of test
// ------------------------------------------------------------------
// func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
// if len(args) != 2 {
// err = fmt.Errorf("%s(): requires exactly two arguments", name)
// return
// }
// x, xok := args[0].(int64)
// y, yok := args[1].(int64)
// if xok && yok {
// result = x - y
// } else {
// err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
// }
// return
// }
func TestEvalStringA(t *testing.T) {
// func TestEvalStringA(t *testing.T) {
source := `a + b * subtract(4,2)`
args := []Arg{
{"a", uint8(1)},
{"b", int8(2)},
{"subtract", FuncTemplate(subtract)},
// force coverage
{"a16", uint16(1)},
{"b16", int16(2)},
{"a32", uint32(1)},
{"b32", int32(2)},
{"a64", uint64(1)},
{"b64", int64(2)},
{"f32", float32(1.0)},
{"f64", float64(1.0)},
}
// source := `a + b * subtract(4,2)`
// args := []Arg{
// {"a", uint8(1)},
// {"b", int8(2)},
// {"subtract", FuncTemplate2(subtract)},
// // force coverage
// {"a16", uint16(1)},
// {"b16", int16(2)},
// {"a32", uint32(1)},
// {"b32", int32(2)},
// {"a64", uint64(1)},
// {"b64", int64(2)},
// {"f32", float32(1.0)},
// {"f64", float64(1.0)},
// }
wantResult := int64(5)
gotResult, gotErr := EvalStringA(source, args...)
if value, ok := gotResult.(int64); ok && value != wantResult {
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
t.Errorf("Error: %v", gotErr)
}
}
// wantResult := int64(5)
// gotResult, gotErr := EvalStringA(source, args...)
// if value, ok := gotResult.(int64); ok && value != wantResult {
// t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
// t.Errorf("Error: %v", gotErr)
// }
// }
func TestEvalString(t *testing.T) {
@@ -68,7 +69,7 @@ func TestEvalString(t *testing.T) {
// force coverage
ctx.GetFuncInfo("dummy")
ctx.Call("dummy", []any{})
ctx.Call("dummy", map[string]any{})
source := `a + b * f`
+4 -2
View File
@@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
/* 4 */ {`"abcdef"[:]`, "abcdef", 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, `[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] invalid range specification`},
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
}
t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 5)
// runTestSuiteSpec(t, section, inputs, 5)
runTestSuite(t, section, inputs)
}
+10 -4
View File
@@ -9,15 +9,15 @@ import "testing"
func TestIteratorParser(t *testing.T) {
section := "Iterator"
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},
/* 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},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, nil, nil},
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
@@ -25,8 +25,14 @@ func TestIteratorParser(t *testing.T) {
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
/* 19 */ {`it=$({1:"one",2:"two",3:"three"}); it++`, int64(1), nil},
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, NewList([]any{int64(1), "one"}), nil},
}
//runTestSuiteSpec(t, section, inputs, 3)
// runTestSuiteSpec(t, section, inputs, 20)
runTestSuite(t, section, inputs)
}
+18 -18
View File
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
/* 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},
/* 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},
/* 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},
/* 14 */ {`[1,2,3][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},
@@ -33,25 +33,25 @@ func TestListParser(t *testing.T) {
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
/* 21 */ {`"b" in ["a", "b", "c"]`, true, 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},
/* 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},
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
/* 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},
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
/* 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, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
/* 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},
/* 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},
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
/* 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},
/* 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},
/* 41 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
/* 27 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`},
/* 28 */ {`2 << 3;`, int64(16), nil},
/* 29 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`},
/* 30 */ {`2 >> 3;`, int64(0), nil},
/* 31 */ {`a=[1,2]; a<+3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 32 */ {`a=[1,2]; 5+>a`, newListA(int64(5), int64(1), int64(2)), nil},
/* 33 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
/* 34 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 35 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
/* 36 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
/* 37 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
/* 38 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
/* 30 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
/* 40 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
}
// t.Setenv("EXPR_PATH", ".")
+20 -1
View File
@@ -14,10 +14,29 @@ func TestOperator(t *testing.T) {
/* 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},
/* 4 */ {`a=1; a<<=1+0`, int64(2), nil},
/* 5 */ {`a=2; a>>=1+0`, int64(1), nil},
/* 6 */ {`1<<1`, int64(2), nil},
/* 7 */ {`1>>1`, int64(0), nil},
/* 8 */ {`1|2`, int64(3), nil},
/* 9 */ {`a=1; a|=2`, int64(3), nil},
/* 10 */ {`3&1`, int64(1), nil},
/* 11 */ {`a=3; a&=1`, int64(1), nil},
/* 12 */ {`~1`, int64(-2), nil},
/* 13 */ {`0x10`, int64(16), nil},
/* 14 */ {`0x1X`, nil, `[1:5] two adjacent operators: "1" and "X"`},
/* 15 */ {`0o10`, int64(8), nil},
/* 16 */ {`0b10`, int64(2), nil},
/* 17 */ {`~true`, nil, `[1:2] prefix/postfix operator "~" do not support operand 'true' [bool]`},
/* 18 */ {`1^2`, int64(3), nil},
/* 19 */ {`3^2`, int64(1), nil},
/* 19 */ {`a=1; a^=2`, int64(3), nil},
/* 20 */ {`a=1; ++a`, int64(2), nil},
/* 21 */ {`a=1; --a`, int64(0), nil},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
// runTestSuiteSpec(t, section, inputs, 4)
runTestSuite(t, section, inputs)
}
+15 -17
View File
@@ -77,9 +77,9 @@ func TestGeneralParser(t *testing.T) {
/* 63 */ {`"1.5" == `, nil, `[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},
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
/* 67 */ {"4 / 0", nil, `division by zero`},
/* 68 */ {"4.0 / 0", nil, `division by zero`},
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
/* 67 */ {"4 / 0", nil, `[1:4] division by zero`},
/* 68 */ {"4.0 / 0", nil, `[1:6] division by zero`},
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
/* 70 */ {`123`, int64(123), nil},
/* 71 */ {`1.`, float64(1.0), nil},
@@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
/* 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},
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
/* 84 */ {`~ 2 > 1`, false, nil},
/* 85 */ {`~ true && true`, false, nil},
/* 86 */ {`~ false || true`, true, nil},
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
/* 84 */ {`NOT 2 > 1`, false, nil},
/* 85 */ {`nOT true && true`, false, nil},
/* 86 */ {`NOT false || true`, true, nil},
/* 87 */ {`false but true`, true, nil},
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
/* 89 */ {`x=2`, int64(2), nil},
@@ -132,18 +132,16 @@ func TestGeneralParser(t *testing.T) {
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
/* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
/* 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`},
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
/* 124 */ {`x ?! (x+1)`, nil, nil},
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 130)
// runTestSuiteSpec(t, section, inputs, 114)
runTestSuite(t, section, inputs)
}
+11 -11
View File
@@ -8,18 +8,18 @@ import (
"testing"
)
func _TestImportPlugin(t *testing.T) {
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
// func TestImportPlugin(t *testing.T) {
// t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
// t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
gotCount, gotErr := importPluginFromSearchPath("json")
if gotCount != 1 {
t.Errorf("Import count: got=%d, want=1", gotCount)
}
if gotErr != nil {
t.Errorf("importPlugin() failed: %v", gotErr)
}
}
// 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)
// }
// }
func TestPluginExists(t *testing.T) {
name := "json"
+7 -7
View File
@@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
/* 8 */ {`true != false`, true, nil},
/* 9 */ {`1.0 != 3.0-2`, false, nil},
/* 10 */ {`[1,2] != [2,1]`, true, nil},
/* 11 */ {`1|2 == 1|3`, false, nil},
/* 12 */ {`1|2 != 1|3`, true, nil},
/* 13 */ {`1|2 == 4|8`, true, nil},
/* 11 */ {`1:2 == 1:3`, false, nil},
/* 12 */ {`1:2 != 1:3`, true, nil},
/* 13 */ {`1:2 == 4:8`, true, nil},
/* 14 */ {`1 < 8`, true, nil},
/* 15 */ {`1 <= 8`, true, nil},
/* 16 */ {`"a" < "b"`, true, nil},
@@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
/* 19 */ {`1.0 <= 8`, true, nil},
/* 20 */ {`1.0 <= 1.0`, true, nil},
/* 21 */ {`1.0 == 1`, true, nil},
/* 22 */ {`1|2 < 1|3`, false, nil},
/* 23 */ {`1|2 <= 1|3`, false, nil},
/* 24 */ {`1|2 > 1|3`, true, nil},
/* 25 */ {`1|2 >= 1|3`, true, nil},
/* 22 */ {`1:2 < 1:3`, false, nil},
/* 23 */ {`1:2 <= 1:3`, false, nil},
/* 24 */ {`1:2 > 1:3`, true, nil},
/* 25 */ {`1:2 >= 1:3`, true, nil},
/* 26 */ {`[1,2,3] > [2]`, true, nil},
/* 27 */ {`[1,2,3] > [9]`, false, nil},
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
+8 -1
View File
@@ -9,6 +9,7 @@ import (
)
func TestStringsParser(t *testing.T) {
section := "String"
inputs := []inputType{
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
/* 2 */ {`"uno" + 2`, `uno2`, nil},
@@ -16,6 +17,12 @@ func TestStringsParser(t *testing.T) {
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
/* 5 */ {`"abc"[1]`, `b`, nil},
/* 6 */ {`#"abc"`, int64(3), nil},
/* 7 */ {`"192.168.0.240" / "."`, newListA("192", "168", "0", "240"), nil},
/* 8 */ {`("192.168.0.240" / ".")[1]`, "168", nil},
/* 9 */ {`"AF3B0Dz" / 2`, newListA("AF", "3B", "0D", "z"), nil},
/* 10 */ {`"AF3B0Dz" / 0`, nil, "[1:12] division by zero"},
}
runTestSuite(t, "String", inputs)
// runTestSuiteSpec(t, section, inputs, 8)
runTestSuite(t, section, inputs)
}
+31 -2
View File
@@ -15,20 +15,26 @@ const (
priRange
priBut
priAssign
priInsert
priOr
priAnd
priNot
priRelational
priBitwiseOr
priBitwiseAnd
priBitwiseNot
priSum
priProduct
priFraction
priSelector
priBinShift
priSign
priFact
priIterValue
priDefault
priIncDec
priDot
priDereference
priValue
)
@@ -53,6 +59,25 @@ type term struct {
evalFunc evalFuncType
}
func (s *term) Clone() (d *term) {
var children []*term
if s.children != nil {
children = make([]*term, len(s.children))
for i, c := range s.children {
children[i] = c.Clone()
}
}
d = &term{
tk: *s.tk.Clone(),
parent: s.parent,
children: children,
position: s.position,
priority: s.priority,
evalFunc: s.evalFunc,
}
return
}
func (term *term) String() string {
var sb strings.Builder
term.toString(&sb)
@@ -179,6 +204,10 @@ func (term *term) errIncompatibleType(value any) error {
term.source(), value, TypeName(value))
}
func (term *term) errDivisionByZero() error {
return term.tk.Errorf("division by zero")
}
func (term *term) Errorf(template string, args ...any) (err error) {
err = term.tk.Errorf(template, args...)
return
@@ -192,11 +221,11 @@ func (term *term) checkOperands() (err error) {
}
case posPrefix:
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
}
case posPostfix:
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
}
case posMultifix:
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
+26 -1
View File
@@ -51,6 +51,10 @@ func NewErrorToken(row, col int, err error) *Token {
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
}
func (tk *Token) Clone() (c *Token) {
return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
}
func (tk *Token) IsEos() bool {
return tk.Sym == SymEos
}
@@ -67,10 +71,18 @@ func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsOneOfA(termSymbols ...Symbol) bool {
return slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsSymbol(sym Symbol) bool {
return tk.Sym == sym
}
func (tk *Token) SetSymbol(sym Symbol) {
tk.Sym = sym
}
func (tk *Token) Errorf(template string, args ...any) (err error) {
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
return
@@ -85,6 +97,15 @@ func (tk *Token) Error() (err error) {
return
}
func (tk *Token) ErrorText() (err string) {
if tk.Sym == SymError {
if msg, ok := tk.Value.(error); ok {
err = msg.Error()
}
}
return
}
func (tk *Token) Errors(msg string) (err error) {
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
return
@@ -96,6 +117,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
}
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
}
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
return
}
+9
View File
@@ -218,6 +218,15 @@ func ToGoInt(value any, description string) (i int, err error) {
return
}
func ToGoString(value any, description string) (s string, err error) {
if s, ok := value.(string); ok {
return s, nil
} else {
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
}
return
}
func ForAll[T, V any](ts []T, fn func(T) V) []V {
result := make([]V, len(ts))
for i, t := range ts {