Compare commits

...

23 Commits

Author SHA1 Message Date
camoroso 7430e9bdf7 Doc: many improvements 2026-07-30 16:15:31 +02:00
camoroso 8084ffbc19 refactored tests based on test-file.txt; this file is no more needed 2026-07-30 16:11:17 +02:00
camoroso e896e06a93 t_builtin-iterator_test.go: added setup and cleanup function that prepare data&files for test. 2026-07-30 15:39:44 +02:00
camoroso 7df7d1463f builtin-os-file-iter-line/byte.go: Clean() function does not reset data content 2026-07-30 15:37:37 +02:00
camoroso 948bad6f96 t_common_test.go: added function fileExists() 2026-07-30 15:35:25 +02:00
camoroso 1c4ba5ef0b builtin-iterator.go: Added function abort() ans fixed typos in error messages 2026-07-30 14:56:00 +02:00
camoroso 648533cbe3 int-iterator is now defined interval 2026-07-28 07:16:49 +02:00
camoroso 8e596d5979 several changes:
1. moved scan/symbol.go to new package sym and adapted all files that reference Symbol type and its values.
2. new type interval defined by begin, end and step values.
3. replaced operator-range.go with operator-interval.go.
4. replaced range operator begin:end with begin..end..step.
5. new implementation of sub-collection extraction based on new interval literal.
2026-07-27 16:01:52 +02:00
camoroso 07aecfc4e7 digest operator assigns the special variable 'last' for each iteration 2026-07-21 09:09:32 +02:00
camoroso 2f616f349b at operator return -1 when fails in lists and arrays; insert operators (<< and >>) changes che container if it is a variable 2026-07-16 07:25:15 +02:00
camoroso 8c55f4ac4b operator at for dict 2026-07-14 06:50:56 +02:00
camoroso bf2c7df0f0 forgot to add the array module. Separated tests on array and list from operators 2026-07-14 06:49:50 +02:00
camoroso 6b561fea45 array and linked-list: at operator 2026-07-13 15:10:51 +02:00
camoroso daca05e637 linked-list: sum and in operators 2026-07-13 14:27:18 +02:00
camoroso 4df222496d linked-list supports index assign and concatenation 2026-07-13 11:34:25 +02:00
camoroso 072fd9af39 completed list-type to array-type conversion and fixed common test module 2026-07-13 10:42:17 +02:00
camoroso 77f36642b4 The name of 'list' has been changed to 'array'; from now on, 'list' will refer to the linked list. 2026-07-12 07:35:28 +02:00
camoroso b6da9bcad4 refactored data-types to reduce plugin sizes 2026-06-08 07:02:56 +02:00
camoroso fec7cc546c Merge branch 'main' into all-in-one 2026-06-07 05:46:44 +02:00
camoroso d8dc2939b4 Expr.adoc: typos 2026-06-06 06:51:17 +02:00
camoroso dba8e01aa0 ecli.doc: correction and improvement 2026-06-06 06:14:58 +02:00
camoroso 6e868b568d Expr.adoc: $$() corrected a wrong sentences 2026-06-03 09:14:17 +02:00
camoroso 73e4ad7b87 Expr.adoc/groupby: group keys are strings 2026-06-03 09:11:51 +02:00
130 changed files with 3360 additions and 2147 deletions
+9 -7
View File
@@ -10,10 +10,12 @@ import (
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
) )
type ListIterator struct { type ListIterator struct {
a *kern.ListType a *array.ArrayType
count int64 count int64
index int64 index int64
start int64 start int64
@@ -21,7 +23,7 @@ type ListIterator struct {
step int64 step int64
} }
func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) { func NewListIterator(list *array.ArrayType, args []any) (it *ListIterator) {
var argc int = 0 var argc int = 0
listLen := int64(len(([]any)(*list))) listLen := int64(len(([]any)(*list)))
if args != nil { if args != nil {
@@ -29,21 +31,21 @@ func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
} }
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1} it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
if argc >= 1 { if argc >= 1 {
if i, err := kern.ToGoInt64(args[0], "start index"); err == nil { if i, err := types.ToGoInt64(args[0], "start index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.start = i it.start = i
} }
if argc >= 2 { if argc >= 2 {
if i, err := kern.ToGoInt64(args[1], "stop index"); err == nil { if i, err := types.ToGoInt64(args[1], "stop index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.stop = i it.stop = i
} }
if argc >= 3 { if argc >= 3 {
if i, err := kern.ToGoInt64(args[2], "step"); err == nil { if i, err := types.ToGoInt64(args[2], "step"); err == nil {
if i < 0 { if i < 0 {
i = -i i = -i
} }
@@ -60,8 +62,8 @@ func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
return return
} }
func NewArrayIterator(array []any) (it *ListIterator) { func NewArrayIterator(a []any) (it *ListIterator) {
it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1} it = &ListIterator{a: (*array.ArrayType)(&a), count: 0, index: -1, start: 0, stop: int64(len(a)) - 1, step: 1}
return return
} }
+39 -25
View File
@@ -12,6 +12,14 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/boolean"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/list"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
const ( const (
@@ -24,42 +32,47 @@ func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result a
} }
func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsInteger(args[kern.ParamValue]) result = types.IsInteger(args[kern.ParamValue])
return return
} }
func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsFloat(args[kern.ParamValue]) result = float.IsFloat(args[kern.ParamValue])
return return
} }
func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsBool(args[kern.ParamValue]) result = boolean.IsBool(args[kern.ParamValue])
return return
} }
func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsString(args[kern.ParamValue]) result = str.IsString(args[kern.ParamValue])
return return
} }
func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsFraction(args[kern.ParamValue]) result = fract.IsFraction(args[kern.ParamValue])
return return
} }
func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsRational(args[kern.ParamValue]) result = fract.IsRational(args[kern.ParamValue])
return return
} }
func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isArrayFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsList(args[kern.ParamValue]) result = array.IsArray(args[kern.ParamValue])
return return
} }
func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = kern.IsDict(args[kern.ParamValue]) result = dict.IsDict(args[kern.ParamValue])
return
}
func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = list.IsLinkedList(args[kern.ParamValue])
return return
} }
@@ -67,7 +80,7 @@ func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
switch v := args[kern.ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = (v != 0) result = (v != 0)
case *kern.FractionType: case *fract.FractionType:
result = v.N() != 0 result = v.N() != 0
case float64: case float64:
result = v != 0.0 result = v != 0.0
@@ -75,9 +88,9 @@ func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
result = v result = v
case string: case string:
result = len(v) > 0 result = len(v) > 0
case *kern.ListType: case *array.ArrayType:
result = len(*v) > 0 result = len(*v) > 0
case *kern.DictType: case *dict.DictType:
result = len(*v) > 0 result = len(*v) > 0
default: default:
err = kern.ErrCantConvert(name, v, "bool") err = kern.ErrCantConvert(name, v, "bool")
@@ -102,7 +115,7 @@ func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
if i, err = strconv.Atoi(v); err == nil { if i, err = strconv.Atoi(v); err == nil {
result = int64(i) result = int64(i)
} }
case *kern.FractionType: case *fract.FractionType:
result = int64(v.N() / v.D()) result = int64(v.N() / v.D())
default: default:
err = kern.ErrCantConvert(name, v, "int") err = kern.ErrCantConvert(name, v, "int")
@@ -110,7 +123,7 @@ func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
return return
} }
func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func floatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[kern.ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = float64(v) result = float64(v)
@@ -127,7 +140,7 @@ func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
if f, err = strconv.ParseFloat(v, 64); err == nil { if f, err = strconv.ParseFloat(v, 64); err == nil {
result = f result = f
} }
case *kern.FractionType: case *fract.FractionType:
result = v.ToFloat() result = v.ToFloat()
default: default:
err = kern.ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
@@ -149,7 +162,7 @@ func stringFunc(ctx kern.ExprContext, name string, args map[string]any) (result
} }
case string: case string:
result = v result = v
case *kern.FractionType: case *fract.FractionType:
result = v.ToString(0) result = v.ToString(0)
case kern.Formatter: case kern.Formatter:
result = v.ToString(0) result = v.ToString(0)
@@ -174,19 +187,19 @@ func fractFunc(ctx kern.ExprContext, name string, args map[string]any) (result a
} }
if err == nil { if err == nil {
result = kern.NewFraction(v, den) result = fract.NewFraction(v, den)
} }
case float64: case float64:
result, err = kern.Float64ToFraction(v) result, err = fract.Float64ToFraction(v)
case bool: case bool:
if v { if v {
result = kern.NewFraction(1, 1) result = fract.NewFraction(1, 1)
} else { } else {
result = kern.NewFraction(0, 1) result = fract.NewFraction(0, 1)
} }
case string: case string:
result, err = kern.MakeGeneratingFraction(v) result, err = fract.MakeGeneratingFraction(v)
case *kern.FractionType: case *fract.FractionType:
result = v result = v
default: default:
err = kern.ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
@@ -276,7 +289,7 @@ func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
} }
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
list := kern.NewLinkedList() list := list.NewLinkedList()
items := args[kern.ParamValue].([]any) items := args[kern.ParamValue].([]any)
for _, arg := range items { for _, arg := range items {
list.PushBack(arg) list.PushBack(arg)
@@ -299,12 +312,13 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("isArray", kern.NewGolangFunctor(isArrayFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams) ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams) ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams) ctx.RegisterFunc("float", kern.NewGolangFunctor(floatFunc), kern.TypeFloat, anyParams)
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams) ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{ ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamValue), kern.NewFuncParam(kern.ParamValue),
+2 -1
View File
@@ -10,6 +10,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
@@ -47,7 +48,7 @@ func doImport(ctx kern.ExprContext, name string, dirList []string, it kern.Itera
var expr *scan.Ast var expr *scan.Ast
scanner := scan.NewScanner(file, scan.DefaultTranslations()) scanner := scan.NewScanner(file, scan.DefaultTranslations())
parser := NewParser() parser := NewParser()
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymEos); err == nil { if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, sym.SymEos); err == nil {
result, err = expr.Eval(ctx) result, err = expr.Eval(ctx)
} }
if err != nil { if err != nil {
+36 -14
View File
@@ -9,32 +9,39 @@ import (
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/boolean"
"git.portale-stac.it/go-pkg/expr/types/dict"
) )
const ( const (
iterParamOperator = "operator" iterParamOperator = "operator"
iterParamVars = "vars" iterParamVars = "vars"
iterVarStatus = "status" iterVarStatus = "status"
iterParamStatus = "status"
iterVarAbort = "__abort"
) )
func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) { func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
var ok bool var ok bool
if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok { if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator])) // err = fmt.Errorf("parameter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
err = errBadParamType(kern.ParamIterator, kern.TypeIterator, args[kern.ParamIterator])
return return
} }
if args[iterParamOperator] != nil { if args[iterParamOperator] != nil {
if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil { if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator])) // err = fmt.Errorf("parameter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
err = errBadParamType(iterParamOperator, "function", args[iterParamOperator])
return return
} }
} }
var vars *kern.DictType var vars *dict.DictType
if vars, ok = args[iterParamVars].(*kern.DictType); !ok && args[iterParamVars] != nil { if vars, ok = args[iterParamVars].(*dict.DictType); !ok && args[iterParamVars] != nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars])) // err = fmt.Errorf("parameter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
err = errBadParamType(iterParamVars, kern.TypeDict, args[iterParamVars])
return return
} }
@@ -50,12 +57,21 @@ func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Itera
return return
} }
func errBadParamType(paramName, wantedType string, wantedValue any) error {
return fmt.Errorf("type of %q must be %s, passed %v [%s]", paramName, wantedType, wantedValue, kern.TypeName(wantedValue))
}
func abortFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if status, provided := args[iterParamStatus]; provided {
ctx.UnsafeSetVar("@"+iterVarStatus, status)
}
ctx.UnsafeSetVar("@"+iterVarAbort, true)
return
}
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var it kern.Iterator var it kern.Iterator
var ok bool
var op kern.Functor var op kern.Functor
var v any
// var usingDefaultOp = false
var params map[string]any var params map[string]any
var item any var item any
@@ -66,14 +82,20 @@ func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
return return
} }
localCtx.RegisterFunc("abort", kern.NewGolangFunctor(abortFunc), kern.TypeAny, []kern.ExprFuncParam{
kern.NewFuncParamFlagDef(iterParamStatus, kern.PfOptional, "aborted"),
})
params = map[string]any{kern.ParamIndex: 0, kern.ParamItem: nil}
for item, err = it.Next(); err == nil; item, err = it.Next() { for item, err = it.Next(); err == nil; item, err = it.Next() {
if op != nil { if op != nil {
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item} // params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil { params[kern.ParamIndex] = it.Index()
params[kern.ParamItem] = item
if _, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break break
} else { } else if abortAny, exists := localCtx.GetVar(iterVarAbort); exists {
var success bool if abort, ok := boolean.ToBool(abortAny); ok && abort {
if success, ok = kern.ToBool(v); !success || !ok {
break break
} }
} }
@@ -85,7 +107,7 @@ func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
} }
if err == nil { if err == nil {
if op == nil { if op == nil {
ctx.UnsafeSetVar(iterVarStatus, it.Count()) localCtx.UnsafeSetVar(iterVarStatus, it.Count())
} }
result, _ = localCtx.GetVar(iterVarStatus) result, _ = localCtx.GetVar(iterVarStatus)
} }
+25 -21
View File
@@ -9,10 +9,14 @@ import (
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
) )
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
if !(kern.IsNumber(paramValue) || kern.IsFraction(paramValue)) /*|| isList(paramValue)*/ { if !(types.IsNumber(paramValue) || fract.IsFraction(paramValue)) /*|| isArray(paramValue)*/ {
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
funcName, paramPos+1, subPos+1, level, paramValue) funcName, paramPos+1, subPos+1, level, paramValue)
} }
@@ -23,13 +27,13 @@ func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
var sumAsFloat, sumAsFract bool var sumAsFloat, sumAsFract bool
var floatSum float64 = 0.0 var floatSum float64 = 0.0
var intSum int64 = 0 var intSum int64 = 0
var fractSum *kern.FractionType var fractSum *fract.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*kern.ListType); ok { if list, ok := v.(*array.ArrayType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(kern.Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
@@ -47,29 +51,29 @@ func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
count++ count++
if !sumAsFloat { if !sumAsFloat {
if kern.IsFloat(v) { if float.IsFloat(v) {
sumAsFloat = true sumAsFloat = true
if sumAsFract { if sumAsFract {
floatSum = fractSum.ToFloat() floatSum = fractSum.ToFloat()
} else { } else {
floatSum = float64(intSum) floatSum = float64(intSum)
} }
} else if !sumAsFract && kern.IsFraction(v) { } else if !sumAsFract && fract.IsFraction(v) {
fractSum = kern.NewFraction(intSum, 1) fractSum = fract.NewFraction(intSum, 1)
sumAsFract = true sumAsFract = true
} }
} }
if sumAsFloat { if sumAsFloat {
floatSum += kern.NumAsFloat(v) floatSum += types.NumAsFloat(v)
} else if sumAsFract { } else if sumAsFract {
var item *kern.FractionType var item *fract.FractionType
var ok bool var ok bool
if item, ok = v.(*kern.FractionType); !ok { if item, ok = v.(*fract.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = kern.NewFraction(iv, 1) item = fract.NewFraction(iv, 1)
} }
fractSum = kern.SumFract(fractSum, item) fractSum = fract.SumFract(fractSum, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intSum += iv intSum += iv
@@ -98,12 +102,12 @@ func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
var mulAsFloat, mulAsFract bool var mulAsFloat, mulAsFract bool
var floatProd float64 = 1.0 var floatProd float64 = 1.0
var intProd int64 = 1 var intProd int64 = 1
var fractProd *kern.FractionType var fractProd *fract.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*kern.ListType); ok { if list, ok := v.(*array.ArrayType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(kern.Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
@@ -123,29 +127,29 @@ func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
count++ count++
if !mulAsFloat { if !mulAsFloat {
if kern.IsFloat(v) { if float.IsFloat(v) {
mulAsFloat = true mulAsFloat = true
if mulAsFract { if mulAsFract {
floatProd = fractProd.ToFloat() floatProd = fractProd.ToFloat()
} else { } else {
floatProd = float64(intProd) floatProd = float64(intProd)
} }
} else if !mulAsFract && kern.IsFraction(v) { } else if !mulAsFract && fract.IsFraction(v) {
fractProd = kern.NewFraction(intProd, 1) fractProd = fract.NewFraction(intProd, 1)
mulAsFract = true mulAsFract = true
} }
} }
if mulAsFloat { if mulAsFloat {
floatProd *= kern.NumAsFloat(v) floatProd *= types.NumAsFloat(v)
} else if mulAsFract { } else if mulAsFract {
var item *kern.FractionType var item *fract.FractionType
var ok bool var ok bool
if item, ok = v.(*kern.FractionType); !ok { if item, ok = v.(*fract.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = kern.NewFraction(iv, 1) item = fract.NewFraction(iv, 1)
} }
fractProd = kern.MulFract(fractProd, item) fractProd = fract.MulFract(fractProd, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intProd *= iv intProd *= iv
+2 -2
View File
@@ -59,8 +59,8 @@ func (it *fileFileByteIterator) Clean() (err error) {
it.reader = nil it.reader = nil
} }
} }
it.reset() // it.reset()
it.b = 0 // it.b = 0
return return
} }
+2 -2
View File
@@ -62,8 +62,8 @@ func (it *fileFileLineIterator) Clean() (err error) {
it.reader = nil it.reader = nil
} }
} }
it.reset() // it.reset()
it.line = "" // it.line = ""
return return
} }
+9 -7
View File
@@ -10,6 +10,8 @@ import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
) )
const ( const (
@@ -43,8 +45,8 @@ func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result
if v, exists := args[kern.ParamItem]; exists { if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any) argv := v.([]any)
if len(argv) == 1 { if len(argv) == 1 {
if ls, ok := argv[0].(*kern.ListType); ok { if a, ok := argv[0].(*array.ArrayType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) result, err = doJoinStr(name, sep, NewListIterator(a, nil))
} else if it, ok := argv[0].(kern.Iterator); ok { } else if it, ok := argv[0].(kern.Iterator); ok {
result, err = doJoinStr(name, sep, it) result, err = doJoinStr(name, sep, it)
} else if s, ok := argv[0].(string); ok { } else if s, ok := argv[0].(string); ok {
@@ -72,11 +74,11 @@ func subStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource]) return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil { if start, err = types.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
return return
} }
if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil { if count, err = types.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
return return
} }
@@ -194,11 +196,11 @@ func splitStrFunc(ctx kern.ExprContext, name string, args map[string]any) (resul
} else { } else {
parts = []string{} parts = []string{}
} }
list := make(kern.ListType, len(parts)) a := make(array.ArrayType, len(parts))
for i, part := range parts { for i, part := range parts {
list[i] = part a[i] = part
} }
result = &list result = &a
return return
} }
+44 -19
View File
@@ -34,6 +34,7 @@ Expression Calculator Interactive Tool
The tool combines the expression evaluation capabilities of the Expr package with an interactive shell environment, making it ideal for: The tool combines the expression evaluation capabilities of the Expr package with an interactive shell environment, making it ideal for:
- Interactive expression testing and prototyping - Interactive expression testing and prototyping
- Command history survival across sessions
- Batch expression evaluation from scripts - Batch expression evaluation from scripts
- Data processing and transformation - Data processing and transformation
- Mathematical computations with fractions and complex operators - Mathematical computations with fractions and complex operators
@@ -61,7 +62,7 @@ Start the interactive REPL:
./ecli ./ecli
---- ----
You'll see the prompt `>>> ` where you can enter expressions to evaluate. You'll see the prompt `>>>` where you can enter expressions to evaluate.
=== Command Line Options === Command Line Options
@@ -89,11 +90,14 @@ Within the REPL, you can use the following commands:
|=== |===
| Command | Description | Command | Description
| `help` | Display available commands and command-line options | `base [2\|8\|10\|16]` | Set the integer output base. Use without arguments to show current base
| `exit` | Exit the REPL | `exit` | Exit the REPL
| `multiline` | Toggle multi-line input mode for complex expressions | `help` | Display available commands and command-line options
| `tty` | Toggle TTY mode | `ml` | Enable/Disable multi-line input mode for complex expressions
| `source <file>` | Execute expressions from a file | `mods` | List all builtin modules (with `*` marking imported ones)
| `output [on\|off\|status]` | Enable/Disable printing of expression results
| `source <file>` | Load and execute expressions from a file
| `tty` | Enable/Disable ANSI terminal output
|=== |===
== Features == Features
@@ -160,8 +164,16 @@ hello world
[source,bash] [source,bash]
---- ----
>>> [1, 2, 3, 4, 5] | map(. * 2) >>> it = [1, 2, 3, 4, 5] map $_ * 2
[2, 4, 6, 8, 10] $($([#5]))
>>> $$(it)
[<
2,
4,
6,
8,
10
>]
---- ----
=== Evaluating from Command Line === Evaluating from Command Line
@@ -192,7 +204,7 @@ Or from command line:
[source,bash] [source,bash]
---- ----
./ecli -e '@include "expressions.expr"' ./ecli -e "@expressions.expr"
---- ----
== Configuration == Configuration
@@ -233,16 +245,16 @@ The build process generates:
=== Multi-line Input === Multi-line Input
For complex expressions, toggle multi-line mode: For complex expressions, split operations across multiple lines terminating with a backslash (`\`):
[source] [source]
---- ----
>>> multiline >>> ml
>>> result = [1, 2, 3, 4, 5] >>> result = [1, 2, 3, 4, 5] \
... | filter(. > 2) ... filter $_ > 2 \
... | map(. * 2) ... map $_ * 2
>>> result >>> $$(result)
[6, 8, 10] [<6, 8, 10>
---- ----
=== Script Execution === Script Execution
@@ -260,16 +272,29 @@ Execute it:
[source,bash] [source,bash]
---- ----
./ecli -e '@source "calculations.expr"' -e 'result' ./ecli -e '@calculations.expr' -e 'result'
50
---- ----
=== Chaining Operations === Other Iterator Examples
[source] [source]
---- ----
>>> data = [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}] >>> data = [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]
>>> data | map(.name)
[alice, bob] // Extract names using map iterator
>>> $$(data map ($_).name)
[<
"alice",
"bob"
>]
// Filter ages greater than 28
>>> $$(data filter ($_).age > 28 map ($_).name)
[<
"alice"
>]
---- ----
== Troubleshooting == Troubleshooting
+3
View File
@@ -3,6 +3,9 @@
//go:build graph //go:build graph
// NOTE: Enable this build tag to print the graph of the expression instead of evaluating it.
// go run -tags graph cmd/ecli/main.go
// graph.go // graph.go
package main package main
+7 -4
View File
@@ -14,6 +14,9 @@ import (
"git.portale-stac.it/go-pkg/expr" "git.portale-stac.it/go-pkg/expr"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/str"
"git.portale-stac.it/go-pkg/utils" "git.portale-stac.it/go-pkg/utils"
// https://pkg.go.dev/github.com/ergochat/readline#section-readme // https://pkg.go.dev/github.com/ergochat/readline#section-readme
@@ -225,10 +228,10 @@ func compute(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool
func printResult(opt *Options, result any) { func printResult(opt *Options, result any) {
if f, ok := result.(kern.Formatter); ok { if f, ok := result.(kern.Formatter); ok {
fmt.Println(f.ToString(opt.formOpt)) fmt.Println(f.ToString(opt.formOpt))
} else if kern.IsInteger(result) { } else if types.IsInteger(result) {
fmt.Printf(opt.baseVerb, result) fmt.Printf(opt.baseVerb, result)
fmt.Println() fmt.Println()
} else if kern.IsString(result) { } else if str.IsString(result) {
fmt.Printf("\"%s\"\n", result) fmt.Printf("\"%s\"\n", result)
} else { } else {
fmt.Println(result) fmt.Println(result)
@@ -257,7 +260,7 @@ func registerLocalFunctions(ctx kern.ExprContext) {
vars := ctx.EnumVars(func(name string) bool { vars := ctx.EnumVars(func(name string) bool {
return len(name) > 0 && name[0] == '_' return len(name) > 0 && name[0] == '_'
}) })
result = kern.ListFromStrings(vars) result = array.ArrayFromStrings(vars)
return return
} }
@@ -309,7 +312,7 @@ func registerLocalFunctions(ctx kern.ExprContext) {
name, _, _ := strings.Cut(e, "=") name, _, _ := strings.Cut(e, "=")
vars = append(vars, name) vars = append(vars, name)
} }
result = kern.ListFromStrings(vars) result = array.ArrayFromStrings(vars)
return return
} }
+9 -6
View File
@@ -11,6 +11,9 @@ import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
type dictIterMode int type dictIterMode int
@@ -22,7 +25,7 @@ const (
) )
type DictIterator struct { type DictIterator struct {
a *kern.DictType a *dict.DictType
count int64 count int64
index int64 index int64
keys []any keys []any
@@ -67,7 +70,7 @@ func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
} }
} }
func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) { func NewDictIterator(dict *dict.DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone var sortType = sortTypeNone
var s string var s string
var argAny any var argAny any
@@ -78,7 +81,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
} else { } else {
argAny = "default" argAny = "default"
} }
if s, err = kern.ToGoString(argAny, "sort type"); err == nil { if s, err = str.ToGoString(argAny, "sort type"); err == nil {
switch strings.ToLower(s) { switch strings.ToLower(s) {
case "a", "asc": case "a", "asc":
sortType = sortTypeAsc sortType = sortTypeAsc
@@ -99,7 +102,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
argAny = "default" argAny = "default"
} }
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil { if s, err = str.ToGoString(argAny, "iteration mode"); err == nil {
switch strings.ToLower(s) { switch strings.ToLower(s) {
case "k", "key", "keys": case "k", "key", "keys":
dictIt.iterMode = dictIterModeKeys dictIt.iterMode = dictIterModeKeys
@@ -124,7 +127,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
} }
func NewMapIterator(m map[any]any) (it *DictIterator) { func NewMapIterator(m map[any]any) (it *DictIterator) {
it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil} it = &DictIterator{a: (*dict.DictType)(&m), count: 0, index: -1, keys: nil}
it.makeKeys(m, sortTypeNone) it.makeKeys(m, sortTypeNone)
return return
} }
@@ -190,7 +193,7 @@ func (it *DictIterator) Current() (item any, err error) {
case dictIterModeItems: case dictIterModeItems:
a := *(it.a) a := *(it.a)
pair := []any{it.keys[it.index], a[it.keys[it.index]]} pair := []any{it.keys[it.index], a[it.keys[it.index]]}
item = kern.NewList(pair) item = array.NewArray(pair)
} }
} else { } else {
err = io.EOF err = io.EOF
+349 -166
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/boolean"
) )
//var globalCtx *SimpleStore //var globalCtx *SimpleStore
@@ -68,7 +69,7 @@ func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) { func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
name = fixCtrlVar(name) name = fixCtrlVar(name)
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) { if v, exists := ctx.GetVar(name); exists && boolean.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
@@ -78,7 +79,7 @@ func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) { func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
name = fixCtrlVar(name) name = fixCtrlVar(name)
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) { if v, exists := ctx.GetVar(name); exists && boolean.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
+4 -2
View File
@@ -12,6 +12,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
) )
@@ -50,9 +52,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
} else { } else {
err = fmt.Errorf("invalid function specification: %q", arg.Name) err = fmt.Errorf("invalid function specification: %q", arg.Name)
} }
} else if integer, ok := kern.AnyInteger(arg.Value); ok { } else if integer, ok := types.AnyInteger(arg.Value); ok {
ctx.SetVar(arg.Name, integer) ctx.SetVar(arg.Name, integer)
} else if float, ok := kern.AnyFloat(arg.Value); ok { } else if float, ok := float.AnyFloat(arg.Value); ok {
ctx.SetVar(arg.Name, float) ctx.SetVar(arg.Name, float)
} else if _, ok := arg.Value.(string); ok { } else if _, ok := arg.Value.(string); ok {
ctx.SetVar(arg.Name, arg.Value) ctx.SetVar(arg.Name, arg.Value)
+2 -1
View File
@@ -13,6 +13,7 @@ import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/str"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
) )
@@ -22,7 +23,7 @@ const (
) )
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(kern.IsString(paramValue) /*|| isList(paramValue)*/) { if !(str.IsString(paramValue) /*|| isArray(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue)) err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
} }
return return
+15 -3
View File
@@ -10,6 +10,8 @@ import (
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/interval"
) )
type IntIterator struct { type IntIterator struct {
@@ -20,6 +22,16 @@ type IntIterator struct {
step int64 step int64
} }
func NewIntIteratorFromInterval(p *interval.IntervalType) (it *IntIterator, err error) {
b := p.Begin()
e := p.End()
s := p.Step()
if b > e {
s = -s
}
return NewIntIteratorA(b, e, s)
}
func NewIntIteratorA(args ...any) (it *IntIterator, err error) { func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
return NewIntIterator(args) return NewIntIterator(args)
} }
@@ -31,16 +43,16 @@ func NewIntIterator(args []any) (it *IntIterator, err error) {
} }
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1} it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
if argc >= 1 { if argc >= 1 {
if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil { if it.stop, err = types.ToGoInt64(args[0], "start index"); err != nil {
return return
} }
if argc >= 2 { if argc >= 2 {
it.start = it.stop it.start = it.stop
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil { if it.stop, err = types.ToGoInt64(args[1], "stop index"); err != nil {
return return
} }
if argc >= 3 { if argc >= 3 {
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil { if it.step, err = types.ToGoInt64(args[2], "step"); err != nil {
return return
} }
} else if it.start > it.stop { } else if it.start > it.stop {
+6 -3
View File
@@ -9,6 +9,9 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
func NewFormalIterator(value any) (it kern.Iterator) { func NewFormalIterator(value any) (it kern.Iterator) {
@@ -26,11 +29,11 @@ func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Ite
} }
switch v := value.(type) { switch v := value.(type) {
case *kern.ListType: case *array.ArrayType:
it = NewListIterator(v, nil) it = NewListIterator(v, nil)
case *kern.LinkedList: case *list.LinkedList:
it = NewLinkedListIterator(v, nil) it = NewLinkedListIterator(v, nil)
case *kern.DictType: case *dict.DictType:
it, err = NewDictIterator(v, nil) it, err = NewDictIterator(v, nil)
case []any: case []any:
it = NewArrayIterator(v) it = NewArrayIterator(v)
-30
View File
@@ -1,30 +0,0 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// clone-value.go
package kern
func Clone(v any) (c any) {
if v == nil {
return
}
switch unboxed := v.(type) {
case int64:
c = unboxed
case float64:
c = unboxed
case string:
c = unboxed
case bool:
c = unboxed
case *ListType:
c = unboxed.Clone()
case *DictType:
c = unboxed.Clone()
case *LinkedList:
c = unboxed.Clone()
default:
c = v
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// cloner.go
package kern
type Cloner interface {
Clone() Cloner
}
func IsCloner(v any) (ok bool) {
_, ok = v.(Cloner)
return
}
func Clone(a any) (clone any) {
if IsCloner(a) {
clone = a.(Cloner).Clone()
} else {
clone = a
}
return
}
+2 -1
View File
@@ -6,6 +6,7 @@ package kern
const ( const (
TypeAny = "any" TypeAny = "any"
TypeArray = "array"
TypeNil = "nil" TypeNil = "nil"
TypeBoolean = "boolean" TypeBoolean = "boolean"
TypeFloat = "float" TypeFloat = "float"
@@ -15,7 +16,7 @@ const (
TypeItem = "item" TypeItem = "item"
TypeIterator = "iterator" TypeIterator = "iterator"
TypeNumber = "number" TypeNumber = "number"
TypePair = "pair" TypeIntPair = "int-pair"
TypeString = "string" TypeString = "string"
TypeDict = "dict" TypeDict = "dict"
TypeListOf = "list-of-" TypeListOf = "list-of-"
-41
View File
@@ -1,41 +0,0 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
package kern
import "reflect"
func Equal(value1, value2 any) (equal bool) {
if value1 == nil && value2 == nil {
equal = true
} else if value1 == nil || value2 == nil {
equal = false
} else if IsBool(value1) && IsBool(value2) {
equal = value1.(bool) == value2.(bool)
} else if IsList(value1) && IsList(value2) {
ls1 := value1.(*ListType)
ls2 := value2.(*ListType)
equal = ls1.Equals(*ls2)
} else if IsDict(value1) && IsDict(value2) {
d1 := value1.(*DictType)
d2 := value2.(*DictType)
equal = d1.Equals(*d2)
} else if IsLinkedList(value1) && IsLinkedList(value2) {
ll1 := value1.(*LinkedList)
ll2 := value2.(*LinkedList)
equal = ll1.Equals(ll2)
} else if IsInteger(value1) && IsInteger(value2) {
equal = value1.(int64) == value2.(int64)
} else if IsString(value1) && IsString(value2) {
equal = value1.(string) == value2.(string)
} else if IsFloat(value1) && IsFloat(value2) {
equal = value1.(float64) == value2.(float64)
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
if eq, err := CmpAnyFract(value1, value2); err == nil {
equal = eq == 0
}
} else if !reflect.DeepEqual(value1, value2) {
equal = false
}
return
}
+7
View File
@@ -0,0 +1,7 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// consts.go
package kern
const MaxUint64Allowed = uint64(9_223_372_036_854_775_807)
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// equaler.go
package kern
type Equaler interface {
EqualTo(other Equaler) bool
}
func IsEqualer(v any) (ok bool) {
_, ok = v.(Equaler)
return
}
func Equal(a, b any) (equal bool) {
if IsEqualer(a) && IsEqualer(b) {
equal = a.(Equaler).EqualTo(b.(Equaler))
} else {
equal = a == b
}
return
}
+1 -30
View File
@@ -29,35 +29,6 @@ type ExprContext interface {
RegisterFuncInfo(info ExprFunc) RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error) RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
ToDict() (dict *DictType) ToDict() (dict any) // must be a *DictType
ToString(opt FmtOpt) string ToString(opt FmtOpt) string
} }
func ContextToDict(ctx ExprContext) (dict *DictType) {
var keys []string
// Variables
keys = ctx.EnumVars(nil)
vars := MakeDict()
for _, key := range keys {
value, _ := ctx.GetVar(key)
vars.SetItem(key, value)
}
// Functions
keys = ctx.EnumFuncs(func(name string) bool { return true })
funcs := MakeDict()
for _, key := range keys {
funcInfo, _ := ctx.GetFuncInfo(key)
funcs.SetItem(key, funcInfo)
}
dict = MakeDict()
dict.SetItem("vars", vars)
dict.SetItem("funcs", funcs)
return
}
func ContextToString(ctx ExprContext, opt FmtOpt) string {
dict := ctx.ToDict()
return dict.ToString(opt)
}
+4 -23
View File
@@ -21,6 +21,10 @@ const (
Base16 Base16
) )
type Formatter interface {
ToString(options FmtOpt) string
}
const ( const (
TruncateEllipsis = "(...)" TruncateEllipsis = "(...)"
MinTruncateSize = 10 MinTruncateSize = 10
@@ -45,10 +49,6 @@ func GetFormatIndent(opt FmtOpt) int {
return int(opt >> 16) return int(opt >> 16)
} }
type Formatter interface {
ToString(options FmtOpt) string
}
func Format(sb *strings.Builder, item any, opt FmtOpt) { func Format(sb *strings.Builder, item any, opt FmtOpt) {
if s, ok := item.(string); ok { if s, ok := item.(string); ok {
sb.WriteByte('"') sb.WriteByte('"')
@@ -73,22 +73,3 @@ func GetFormatted(v any, opt FmtOpt) (text string) {
} }
return return
} }
type Typer interface {
TypeName() string
}
func TypeName(v any) (name string) {
if v == nil {
name = "nil"
} else if typer, ok := v.(Typer); ok {
name = typer.TypeName()
} else if IsInteger(v) {
name = "integer"
} else if IsFloat(v) {
name = "float"
} else {
name = fmt.Sprintf("%T", v)
}
return
}
-203
View File
@@ -1,203 +0,0 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// list-type.go
package kern
import (
"fmt"
"reflect"
"strings"
)
type ListType []any
func IsList(v any) (ok bool) {
_, ok = v.(*ListType)
return ok
}
func NewListA(listAny ...any) (list *ListType) {
if listAny == nil {
listAny = []any{}
}
return NewList(listAny)
}
func NewList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
copy(ls, listAny)
list = &ls
}
return
}
func MakeList(length, capacity int) (list *ListType) {
if capacity < length {
capacity = length
}
ls := make(ListType, length, capacity)
list = &ls
return
}
func ListFromStrings(stringList []string) (list *ListType) {
list = MakeList(len(stringList), 0)
for i, s := range stringList {
(*list)[i] = s
}
return
}
func (ls *ListType) ToString(opt FmtOpt) (s string) {
indent := GetFormatIndent(opt)
flags := GetFormatFlags(opt)
var sb strings.Builder
sb.WriteByte('[')
if len(*ls) > 0 {
innerOpt := MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1)
if flags&MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(nest)
}
for i, item := range []any(*ls) {
if i > 0 {
if flags&MultiLine != 0 {
sb.WriteString(",\n")
sb.WriteString(nest)
} else {
sb.WriteString(", ")
}
}
Format(&sb, item, innerOpt)
}
if flags&MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
}
}
sb.WriteByte(']')
s = sb.String()
if flags&Truncate != 0 && len(s) > TruncateSize {
s = TruncateString(s)
}
return
}
func (ls *ListType) String() string {
return ls.ToString(0)
}
func (ls *ListType) TypeName() string {
return "list"
}
func (ls *ListType) Contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) {
answer = true
for _, item := range *t {
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
break
}
}
}
return
}
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls) == len(ls2) {
answer = true
for index, i1 := range *ls {
// if !reflect.DeepEqual(i1, ls2[index]) {
// answer = false
// break
// }
if !Equal(i1, ls2[index]) {
answer = false
break
}
}
}
return
}
func (ls1 *ListType) Clone() (ls2 *ListType) {
ls := make(ListType, len(*ls1))
for i, item := range *ls1 {
ls[i] = Clone(item)
}
ls2 = &ls
return
}
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
var eq bool
var err error
index = -1
for i, item := range *ls {
if eq, err = deepSame(item, target, SameContent); err != nil {
break
} else if eq {
index = i
break
}
}
return
}
func SameContent(a, b any) (same bool, err error) {
la, _ := a.(*ListType)
lb, _ := b.(*ListType)
if len(*la) == len(*lb) {
same = true
for _, item := range *la {
if pos := lb.IndexDeepSameCmp(item); pos < 0 {
same = false
break
}
}
}
return
}
func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
if IsNumOrFract(a) && IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) {
if IsInteger(a) && IsInteger(b) {
li, _ := a.(int64)
ri, _ := b.(int64)
eq = li == ri
} else {
eq = NumAsFloat(a) == NumAsFloat(b)
}
} else {
var cmp int
if cmp, err = CmpAnyFract(a, b); err == nil {
eq = cmp == 0
}
}
} else if deepCmp != nil && IsList(a) && IsList(b) {
eq, err = deepCmp(a, b)
} else {
eq = reflect.DeepEqual(a, b)
}
return
}
func (ls *ListType) SetItem(index int64, value any) (err error) {
if index >= 0 && index < int64(len(*ls)) {
(*ls)[index] = value
} else {
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
}
return
}
func (ls *ListType) AppendItem(value any) {
*ls = append(*ls, value)
}
+12 -1
View File
@@ -6,6 +6,8 @@ package kern
import ( import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/sym"
) )
type Term interface { type Term interface {
@@ -15,7 +17,16 @@ type Term interface {
GetChildCount() (count int) GetChildCount() (count int)
GetChild(index int) Term GetChild(index int) Term
GetChildSource(index int) string GetChildSource(index int) string
Compute(ctx ExprContext) (result any, err error) GetLeftChild() (c Term)
GetRightChild() (c Term)
Symbol() (symbol sym.Symbol)
IsSymbol(symbol sym.Symbol) bool
IsOneOf(symbols ...sym.Symbol) bool
IsAssign() bool IsAssign() bool
IsVar() bool
SetSymbol(symbol sym.Symbol)
Compute(ctx ExprContext) (result any, err error)
EvalInfix(ctx ExprContext) (leftValue, rightValue any, err error)
Errorf(template string, args ...any) (err error) Errorf(template string, args ...any) (err error)
ErrIncompatibleTypes(leftValue, rightValue any) error
} }
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// typer.go
package kern
import "fmt"
type Typer interface {
TypeName() string
}
func TypeName(v any) (name string) {
if v == nil {
name = "nil"
} else if typer, ok := v.(Typer); ok {
name = typer.TypeName()
} else if _, ok := v.(int64); ok {
name = "integer"
} else if _, ok := v.(float64); ok {
name = "float"
} else {
name = fmt.Sprintf("%T", v)
}
return
}
+37
View File
@@ -0,0 +1,37 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// util.go
package kern
func FixAnyNumber(v any) (fixed any) {
switch unboxed := v.(type) {
case int:
fixed = int64(unboxed)
case int8:
fixed = int64(unboxed)
case int16:
fixed = int64(unboxed)
case int32:
fixed = int64(unboxed)
case uint:
fixed = int64(unboxed)
case uint8:
fixed = int64(unboxed)
case uint16:
fixed = int64(unboxed)
case uint32:
fixed = int64(unboxed)
case uint64:
if unboxed <= MaxUint64Allowed {
fixed = int64(unboxed)
} else {
fixed = float64(unboxed)
}
case float32:
fixed = float64(unboxed)
default:
fixed = v
}
return
}
+4 -3
View File
@@ -10,16 +10,17 @@ import (
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
type LinkedListIterator struct { type LinkedListIterator struct {
a *kern.LinkedList a *list.LinkedList
count int64 count int64
index int64 index int64
current *kern.ListNode current *list.ListNode
} }
func NewLinkedListIterator(list *kern.LinkedList, args []any) (it *LinkedListIterator) { func NewLinkedListIterator(list *list.LinkedList, args []any) (it *LinkedListIterator) {
it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()} it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()}
return return
} }
+9 -5
View File
@@ -7,12 +7,16 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
// -------- dict term // -------- dict term
func newDictTerm(args map[any]*scan.Term) *scan.Term { func newDictTerm(args map[any]*scan.Term) *scan.Term {
return &scan.Term{ return &scan.Term{
Tk: *scan.NewValueToken(0, 0, scan.SymDict, "{}", args), Tk: *scan.NewValueToken(0, 0, sym.SymDict, "{}", args),
Parent: nil, Parent: nil,
Children: nil, Children: nil,
Position: scan.PosLeaf, Position: scan.PosLeaf,
@@ -23,16 +27,16 @@ func newDictTerm(args map[any]*scan.Term) *scan.Term {
// -------- dict func // -------- dict func
func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
dict, _ := opTerm.Value().(map[any]*scan.Term) dt, _ := opTerm.Value().(map[any]*scan.Term)
items := make(kern.DictType, len(dict)) items := make(dict.DictType, len(dt))
for key, tree := range dict { for key, tree := range dt {
var param any var param any
if param, err = tree.Compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
} }
var keyValue any var keyValue any
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil { if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
if kern.IsInteger(keyValue) || kern.IsString(keyValue) { if types.IsInteger(keyValue) || str.IsString(keyValue) {
items[keyValue] = param items[keyValue] = param
} else { } else {
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue)) err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
+2 -1
View File
@@ -9,11 +9,12 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
// -------- expr term // -------- expr term
func newExprTerm(root *scan.Term) *scan.Term { func newExprTerm(root *scan.Term) *scan.Term {
tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymExpression, root.Source(), root) tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), sym.SymExpression, root.Source(), root)
return &scan.Term{ return &scan.Term{
Tk: *tk, Tk: *tk,
Parent: nil, Parent: nil,
+19 -12
View File
@@ -10,12 +10,17 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/interval"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
// -------- iterator term // -------- iterator term
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term { func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
tk.Sym = scan.SymIterator tk.Sym = sym.SymIterator
return &scan.Term{ return &scan.Term{
Tk: *tk, Tk: *tk,
Parent: nil, Parent: nil,
@@ -52,7 +57,7 @@ func evalFirstChild(ctx kern.ExprContext, iteratorTerm *scan.Term) (value any, e
} }
func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) { func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
if dictAny, ok := firstChildValue.(*kern.DictType); ok { if dictAny, ok := firstChildValue.(*dict.DictType); ok {
requiredFields := []string{kern.NextName} requiredFields := []string{kern.NextName}
fieldsMask := 0b1 fieldsMask := 0b1
foundFields := 0 foundFields := 0
@@ -123,7 +128,7 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
v = dc v = dc
} else { } else {
if dictIt, ok := firstChildValue.(*kern.DictType); ok { if dictIt, ok := firstChildValue.(*dict.DictType); ok {
var args []any var args []any
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v, err = NewDictIterator(dictIt, args) v, err = NewDictIterator(dictIt, args)
@@ -132,21 +137,23 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
err = opTerm.Children[0].Errorf("the data-source must be a dictionary") err = opTerm.Children[0].Errorf("the data-source must be a dictionary")
} }
} }
} else if list, ok := firstChildValue.(*kern.ListType); ok { } else if a, ok := firstChildValue.(*array.ArrayType); ok {
var args []any var args []any
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v = NewListIterator(list, args) v = NewListIterator(a, args)
} }
} else if list, ok := firstChildValue.(*kern.LinkedList); ok { } else if ll, ok := firstChildValue.(*list.LinkedList); ok {
var args []any var args []any
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v = NewLinkedListIterator(list, args) v = NewLinkedListIterator(ll, args)
}
} else if intVal, ok := firstChildValue.(int64); ok {
var args []any
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
v, err = NewIntIterator(args)
} }
} else if intervalVal, ok := firstChildValue.(*interval.IntervalType); ok {
v, err = NewIntIteratorFromInterval(intervalVal)
// } else if intVal, ok := firstChildValue.(int64); ok {
// var args []any
// if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
// v, err = NewIntIterator(args)
// }
} else if it, ok := firstChildValue.(kern.Iterator); ok { } else if it, ok := firstChildValue.(kern.Iterator); ok {
v, err = NewIterIter(it, ctx, opTerm.Children[1:]) v, err = NewIterIter(it, ctx, opTerm.Children[1:])
} else { } else {
+6 -4
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
// -------- list term // -------- list term
@@ -16,7 +18,7 @@ import (
func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term { func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
return &scan.Term{ return &scan.Term{
Tk: *scan.NewValueToken(row, col, scan.SymLinkedList, "[<>]", args), Tk: *scan.NewValueToken(row, col, sym.SymLinkedList, "[<>]", args),
Parent: nil, Parent: nil,
Children: nil, Children: nil,
Position: scan.PosLeaf, Position: scan.PosLeaf,
@@ -27,9 +29,9 @@ func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
// -------- list func // -------- list func
func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
list, _ := opTerm.Value().([]*scan.Term) listTerm, _ := opTerm.Value().([]*scan.Term)
items := kern.NewLinkedList() items := list.NewLinkedList()
for _, tree := range list { for _, tree := range listTerm {
var param any var param any
if param, err = tree.Compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
+4 -2
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
) )
// -------- list term // -------- list term
@@ -16,7 +18,7 @@ func newListTermA(args ...*scan.Term) *scan.Term {
func newListTerm(row, col int, args []*scan.Term) *scan.Term { func newListTerm(row, col int, args []*scan.Term) *scan.Term {
return &scan.Term{ return &scan.Term{
Tk: *scan.NewValueToken(row, col, scan.SymList, "[]", args), Tk: *scan.NewValueToken(row, col, sym.SymList, "[]", args),
Parent: nil, Parent: nil,
Children: nil, Children: nil,
Position: scan.PosLeaf, Position: scan.PosLeaf,
@@ -28,7 +30,7 @@ func newListTerm(row, col int, args []*scan.Term) *scan.Term {
// -------- list func // -------- list func
func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
list, _ := opTerm.Value().([]*scan.Term) list, _ := opTerm.Value().([]*scan.Term)
items := make(kern.ListType, len(list)) items := make(array.ArrayType, len(list))
for i, tree := range list { for i, tree := range list {
var param any var param any
if param, err = tree.Compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
+18 -6
View File
@@ -7,10 +7,22 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/fract"
) )
// -------- literal term // -------- literal term
func newLiteralTerm(tk *scan.Token) *scan.Term { func newLiteralTerm(tk *scan.Token) *scan.Term {
if tk.Sym == sym.SymFraction {
if v, isString := tk.Value.(string); isString {
var err error
if tk.Value, err = fract.MakeGeneratingFraction(v); err != nil {
return nil
}
}
}
return &scan.Term{ return &scan.Term{
Tk: *tk, Tk: *tk,
Parent: nil, Parent: nil,
@@ -29,10 +41,10 @@ func evalLiteral(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymString, newLiteralTerm) scan.RegisterTermConstructor(sym.SymString, newLiteralTerm)
scan.RegisterTermConstructor(scan.SymInteger, newLiteralTerm) scan.RegisterTermConstructor(sym.SymInteger, newLiteralTerm)
scan.RegisterTermConstructor(scan.SymFloat, newLiteralTerm) scan.RegisterTermConstructor(sym.SymFloat, newLiteralTerm)
scan.RegisterTermConstructor(scan.SymFraction, newLiteralTerm) scan.RegisterTermConstructor(sym.SymFraction, newLiteralTerm)
scan.RegisterTermConstructor(scan.SymBool, newLiteralTerm) scan.RegisterTermConstructor(sym.SymBool, newLiteralTerm)
scan.RegisterTermConstructor(scan.SymKwNil, newLiteralTerm) scan.RegisterTermConstructor(sym.SymKwNil, newLiteralTerm)
} }
+22 -19
View File
@@ -5,11 +5,9 @@
package expr package expr
import ( import (
"fmt"
"strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
// -------- selector case term // -------- selector case term
@@ -19,20 +17,20 @@ type selectorCase struct {
caseExpr kern.Expr caseExpr kern.Expr
} }
func (sc *selectorCase) String() string { // func (sc *selectorCase) String() string {
var sb strings.Builder // var sb strings.Builder
if sc.filterList != nil { // if sc.filterList != nil {
sc.filterList.ToString(&sb) // sc.filterList.ToString(&sb)
sb.WriteByte(' ') // sb.WriteByte(' ')
} // }
sb.WriteByte('{') // sb.WriteByte('{')
sb.WriteString(sc.caseExpr.String()) // sb.WriteString(sc.caseExpr.String())
sb.WriteByte('}') // sb.WriteByte('}')
return sb.String() // return sb.String()
} // }
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term { func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
tk := scan.NewValueToken(row, col, scan.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr}) tk := scan.NewValueToken(row, col, sym.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
return &scan.Term{ return &scan.Term{
Tk: *tk, Tk: *tk,
Parent: nil, Parent: nil,
@@ -45,9 +43,14 @@ func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var ok bool v, _ = opTerm.Value().(*selectorCase)
if v, ok = opTerm.Value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
}
return return
} }
// func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// var ok bool
// if v, ok = opTerm.Value().(*selectorCase); !ok {
// err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
// }
// return
// }
+3 -2
View File
@@ -9,6 +9,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
// -------- variable term // -------- variable term
@@ -21,7 +22,7 @@ func newVarTerm(tk *scan.Token) *scan.Term {
Priority: scan.PriValue, Priority: scan.PriValue,
EvalFunc: evalVar, EvalFunc: evalVar,
} }
t.Tk.Sym = scan.SymVariable t.Tk.Sym = sym.SymVariable
return t return t
} }
@@ -41,5 +42,5 @@ func evalVar(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymIdentifier, newVarTerm) scan.RegisterTermConstructor(sym.SymIdentifier, newVarTerm)
} }
+51 -41
View File
@@ -7,6 +7,10 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
) )
@@ -44,7 +48,7 @@ func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err err
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) { func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
var collectionValue, keyListValue, keyValue any var collectionValue, keyListValue, keyValue any
var keyList *kern.ListType var keyList *array.ArrayType
var ok bool var ok bool
if collectionValue, err = collectionTerm.Compute(ctx); err != nil { if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
@@ -53,7 +57,7 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
if keyListValue, err = keyListTerm.Compute(ctx); err != nil { if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*array.ArrayType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return return
} }
@@ -63,14 +67,20 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
} }
switch collection := collectionValue.(type) { switch collection := collectionValue.(type) {
case *kern.ListType: case *array.ArrayType:
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
err = collection.SetItem(index, value) err = collection.SetItem(index, value)
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
} }
case *kern.DictType: case *dict.DictType:
collection.SetItem(keyValue, value) collection.SetItem(keyValue, value)
case *list.LinkedList:
if index, ok := keyValue.(int64); ok {
err = collection.SetItem(index, value)
} else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
}
default: default:
err = collectionTerm.Errorf("collection expected") err = collectionTerm.Errorf("collection expected")
} }
@@ -78,7 +88,7 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
} }
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) { func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) {
if leftTerm.Symbol() == scan.SymIndex { if leftTerm.Symbol() == sym.SymIndex {
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v) err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
} else { } else {
if deepCopy { if deepCopy {
@@ -92,7 +102,7 @@ func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) { func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
var ok bool var ok bool
var dictAny, dotKey any var dictAny, dotKey any
var dict *kern.DictType var dt *dict.DictType
if err = dotTerm.CheckOperands(); err != nil { if err = dotTerm.CheckOperands(); err != nil {
return return
@@ -102,14 +112,14 @@ func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil { if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
return return
} }
if dict, ok = dictAny.(*kern.DictType); !ok { if dt, ok = dictAny.(*dict.DictType); !ok {
err = dotTerm.Tk.ErrorExpectedGot(kern.DictTypeName) err = dotTerm.Tk.ErrorExpectedGot(dict.DictTypeName)
return return
} }
dotRightTerm := dotTerm.Children[1] dotRightTerm := dotTerm.Children[1]
dotRightSym := dotRightTerm.Symbol() dotRightSym := dotRightTerm.Symbol()
if dotRightSym == scan.SymVariable || dotRightSym == scan.SymString { if dotRightSym == sym.SymVariable || dotRightSym == sym.SymString {
dotKey = util.UnquoteString(dotRightTerm.Source()) dotKey = util.UnquoteString(dotRightTerm.Source())
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil { } else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
return return
@@ -119,7 +129,7 @@ func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern
return return
} }
dict.SetItem(dotKey, v) dt.SetItem(dotKey, v)
return return
} }
@@ -130,9 +140,9 @@ func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (
leftTerm := opTerm.Children[0] leftTerm := opTerm.Children[0]
leftSym := leftTerm.Symbol() leftSym := leftTerm.Symbol()
if leftSym == scan.SymDot { if leftSym == sym.SymDot {
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1)) return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
} else if leftSym != scan.SymVariable && leftSym != scan.SymIndex { } else if leftSym != sym.SymVariable && leftSym != sym.SymIndex {
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source()) err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
return return
} }
@@ -141,7 +151,7 @@ func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (
if v, err = rightChild.Compute(ctx); err == nil { if v, err = rightChild.Compute(ctx); err == nil {
if functor, ok := v.(kern.Functor); ok { if functor, ok := v.(kern.Functor); ok {
if leftSym == scan.SymVariable { if leftSym == sym.SymVariable {
if info := functor.GetFunc(); info != nil { if info := functor.GetFunc(); info != nil {
ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
} else if funcDef, ok := functor.(*exprFunctor); ok { } else if funcDef, ok := functor.(*exprFunctor); ok {
@@ -178,7 +188,7 @@ func newOpAssignTerm(tk *scan.Token) (inst *scan.Term) {
func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) { func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) {
var collectionValue, keyListValue, keyValue any var collectionValue, keyListValue, keyValue any
var keyList *kern.ListType var keyList *array.ArrayType
var ok bool var ok bool
if collectionValue, err = collectionTerm.Compute(ctx); err != nil { if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
@@ -187,7 +197,7 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
if keyListValue, err = keyListTerm.Compute(ctx); err != nil { if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*array.ArrayType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return return
} }
@@ -197,13 +207,13 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
} }
switch collection := collectionValue.(type) { switch collection := collectionValue.(type) {
case *kern.ListType: case *array.ArrayType:
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
value = (*collection)[index] value = (*collection)[index]
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
} }
case *kern.DictType: case *dict.DictType:
value = (*collection)[keyValue] value = (*collection)[keyValue]
default: default:
err = collectionTerm.Errorf("collection expected") err = collectionTerm.Errorf("collection expected")
@@ -212,7 +222,7 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
} }
func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) { func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) {
if leftTerm.Symbol() == scan.SymIndex { if leftTerm.Symbol() == sym.SymIndex {
value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1]) value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1])
} else { } else {
value, _ = ctx.GetVar(leftTerm.Source()) value, _ = ctx.GetVar(leftTerm.Source())
@@ -228,7 +238,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
leftTerm := opTerm.Children[0] leftTerm := opTerm.Children[0]
leftSym := leftTerm.Symbol() leftSym := leftTerm.Symbol()
if leftSym != scan.SymVariable && leftSym != scan.SymIndex { if leftSym != sym.SymVariable && leftSym != sym.SymIndex {
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source()) err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
return return
} }
@@ -238,25 +248,25 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if rightValue, err = rightChild.Compute(ctx); err == nil { if rightValue, err = rightChild.Compute(ctx); err == nil {
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil { if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
switch opTerm.Symbol() { switch opTerm.Symbol() {
case scan.SymPlusEqual: case sym.SymPlusEqual:
v, err = sumValues(opTerm, leftValue, rightValue) v, err = sumValues(opTerm, leftValue, rightValue)
case scan.SymMinusEqual: case sym.SymMinusEqual:
v, err = diffValues(opTerm, leftValue, rightValue) v, err = diffValues(opTerm, leftValue, rightValue)
case scan.SymStarEqual: case sym.SymStarEqual:
v, err = mulValues(opTerm, leftValue, rightValue) v, err = mulValues(opTerm, leftValue, rightValue)
case scan.SymSlashEqual: case sym.SymSlashEqual:
v, err = divValues(opTerm, leftValue, rightValue) v, err = divValues(opTerm, leftValue, rightValue)
case scan.SymPercEqual: case sym.SymPercEqual:
v, err = remainderValues(opTerm, leftValue, rightValue) v, err = remainderValues(opTerm, leftValue, rightValue)
case scan.SymAmpersandEqual: case sym.SymAmpersandEqual:
v, err = bitwiseAnd(opTerm, leftValue, rightValue) v, err = bitwiseAnd(opTerm, leftValue, rightValue)
case scan.SymVertBarEqual: case sym.SymVertBarEqual:
v, err = bitwiseOr(opTerm, leftValue, rightValue) v, err = bitwiseOr(opTerm, leftValue, rightValue)
case scan.SymCaretEqual: case sym.SymCaretEqual:
v, err = bitwiseXor(opTerm, leftValue, rightValue) v, err = bitwiseXor(opTerm, leftValue, rightValue)
case scan.SymDoubleLessEqual: case sym.SymDoubleLessEqual:
v, err = bitLeftShift(opTerm, leftValue, rightValue) v, err = bitLeftShift(opTerm, leftValue, rightValue)
case scan.SymDoubleGreaterEqual: case sym.SymDoubleGreaterEqual:
v, err = bitRightShift(opTerm, leftValue, rightValue) v, err = bitRightShift(opTerm, leftValue, rightValue)
default: default:
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source()) err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
@@ -271,16 +281,16 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm) scan.RegisterTermConstructor(sym.SymEqual, newAssignTerm)
scan.RegisterTermConstructor(scan.SymColonEqual, newDeepCopyAssignTerm) scan.RegisterTermConstructor(sym.SymColonEqual, newDeepCopyAssignTerm)
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymPlusEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymMinusEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymStarEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymSlashEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymPercEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymDoubleLessEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymDoubleGreaterEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymAmpersandEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymVertBarEqual, newOpAssignTerm)
scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm) scan.RegisterTermConstructor(sym.SymCaretEqual, newOpAssignTerm)
} }
+68
View File
@@ -0,0 +1,68 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-at.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
)
//-------- at term
func newAtTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriRelational,
EvalFunc: evalAt,
}
}
// func hasKey(d map[any]any, target any) (ok bool) {
// _, ok = d[target]
// return
// }
func evalAt(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return
}
v = int64(-1) // default value if not found
if array.IsArray(rightValue) {
a, _ := rightValue.(*array.ArrayType)
if index := a.IndexDeepSameCmp(leftValue); index >= 0 {
v = index
}
} else if dict.IsDict(rightValue) {
dict, _ := rightValue.(*dict.DictType)
if k, exists := dict.FindKey(leftValue); exists {
v = k
} else {
v = nil
}
} else if list.IsLinkedList(rightValue) {
ls, _ := rightValue.(*list.LinkedList)
if index := ls.Index(leftValue); index >= 0 {
v = index
}
} else {
v = nil
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
return
}
// init
func init() {
scan.RegisterTermConstructor(sym.SymKwAt, newAtTerm)
}
+7 -5
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
) )
//-------- Bitwise NOT term //-------- Bitwise NOT term
@@ -28,7 +30,7 @@ func evalBitwiseNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
return return
} }
if kern.IsInteger(value) { if types.IsInteger(value) {
i, _ := value.(int64) i, _ := value.(int64)
v = ^i v = ^i
} else { } else {
@@ -152,8 +154,8 @@ func evalBitwiseXor(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymTilde, newBitwiseNotTerm) scan.RegisterTermConstructor(sym.SymTilde, newBitwiseNotTerm)
scan.RegisterTermConstructor(scan.SymAmpersand, newBitwiseAndTerm) scan.RegisterTermConstructor(sym.SymAmpersand, newBitwiseAndTerm)
scan.RegisterTermConstructor(scan.SymVertBar, newBitwiseOrTerm) scan.RegisterTermConstructor(sym.SymVertBar, newBitwiseOrTerm)
scan.RegisterTermConstructor(scan.SymCaret, newBitwiseXorTerm) scan.RegisterTermConstructor(sym.SymCaret, newBitwiseXorTerm)
} }
+14 -12
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/boolean"
) )
//-------- NOT term //-------- NOT term
@@ -30,7 +32,7 @@ func evalNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if b, ok := kern.ToBool(rightValue); ok { if b, ok := boolean.ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue) err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
@@ -68,8 +70,8 @@ func evalAndWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err e
return return
} }
leftBool, lok = kern.ToBool(leftValue) leftBool, lok = boolean.ToBool(leftValue)
rightBool, rok = kern.ToBool(rightValue) rightBool, rok = boolean.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool && rightBool v = leftBool && rightBool
@@ -90,14 +92,14 @@ func evalAndWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err erro
return return
} }
if leftBool, lok := kern.ToBool(leftValue); !lok { if leftBool, lok := boolean.ToBool(leftValue); !lok {
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue)) // err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
// return // return
err = self.ErrIncompatibleType(leftValue, "left") err = self.ErrIncompatibleType(leftValue, "left")
} else if !leftBool { } else if !leftBool {
v = false v = false
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil { } else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
if rightBool, rok := kern.ToBool(rightValue); rok { if rightBool, rok := boolean.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.ErrIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
@@ -136,8 +138,8 @@ func evalOrWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err er
return return
} }
leftBool, lok = kern.ToBool(leftValue) leftBool, lok = boolean.ToBool(leftValue)
rightBool, rok = kern.ToBool(rightValue) rightBool, rok = boolean.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool || rightBool v = leftBool || rightBool
@@ -158,13 +160,13 @@ func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error
return return
} }
if leftBool, lok := kern.ToBool(leftValue); !lok { if leftBool, lok := boolean.ToBool(leftValue); !lok {
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue)) err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
return return
} else if leftBool { } else if leftBool {
v = true v = true
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil { } else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
if rightBool, rok := kern.ToBool(rightValue); rok { if rightBool, rok := boolean.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.ErrIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
@@ -175,7 +177,7 @@ func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymNot, newNotTerm) scan.RegisterTermConstructor(sym.SymNot, newNotTerm)
scan.RegisterTermConstructor(scan.SymAnd, newAndTerm) scan.RegisterTermConstructor(sym.SymAnd, newAndTerm)
scan.RegisterTermConstructor(scan.SymOr, newOrTerm) scan.RegisterTermConstructor(sym.SymOr, newOrTerm)
} }
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- builtin term //-------- builtin term
@@ -31,7 +33,7 @@ func evalBuiltin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
count := 0 count := 0
if kern.IsString(childValue) { if str.IsString(childValue) {
module, _ := childValue.(string) module, _ := childValue.(string)
count, err = ImportInContextByGlobPattern(ctx, module) count, err = ImportInContextByGlobPattern(ctx, module)
} else { } else {
@@ -65,5 +67,5 @@ func evalBuiltin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwBuiltin, newBuiltinTerm) scan.RegisterTermConstructor(sym.SymKwBuiltin, newBuiltinTerm)
} }
+2 -1
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- but term //-------- but term
@@ -28,5 +29,5 @@ func evalBut(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwBut, newButTerm) scan.RegisterTermConstructor(sym.SymKwBut, newButTerm)
} }
+2 -1
View File
@@ -10,6 +10,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- cat term //-------- cat term
@@ -147,5 +148,5 @@ func (it *catIterator) CallOperation(name string, args map[string]any) (v any, e
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwCat, newCatTerm) scan.RegisterTermConstructor(sym.SymKwCat, newCatTerm)
} }
+5 -3
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
//-------- context term //-------- context term
@@ -29,7 +31,7 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
if len(opTerm.Children) == 0 { if len(opTerm.Children) == 0 {
sourceCtx = ctx sourceCtx = ctx
} else if opTerm.Children[0].Symbol() == scan.SymVariable && opTerm.Children[0].Source() == "global" { } else if opTerm.Children[0].Symbol() == sym.SymVariable && opTerm.Children[0].Source() == "global" {
sourceCtx = ctx.GetGlobal() sourceCtx = ctx.GetGlobal()
} else if childValue, err = opTerm.EvalPrefix(ctx); err == nil { } else if childValue, err = opTerm.EvalPrefix(ctx); err == nil {
if dc, ok := childValue.(*dataCursor); ok { if dc, ok := childValue.(*dataCursor); ok {
@@ -48,7 +50,7 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
} }
if err == nil { if err == nil {
var item any var item any
values := kern.NewLinkedListA() values := list.NewLinkedListA()
for item, err = it.Next(); err == nil; item, err = it.Next() { for item, err = it.Next(); err == nil; item, err = it.Next() {
values.PushBack(item) values.PushBack(item)
} }
@@ -65,5 +67,5 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoubleDollar, newContextTerm) scan.RegisterTermConstructor(sym.SymDoubleDollar, newContextTerm)
} }
+2 -1
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- export all term //-------- export all term
@@ -28,5 +29,5 @@ func evalExportAll(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoubleAt, newExportAllTerm) scan.RegisterTermConstructor(sym.SymDoubleAt, newExportAllTerm)
} }
+7 -6
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- default term //-------- default term
@@ -29,7 +30,7 @@ func evalDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
leftTerm := opTerm.Children[0] leftTerm := opTerm.Children[0]
if leftTerm.Tk.Sym != scan.SymVariable { if leftTerm.Tk.Sym != sym.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
@@ -63,7 +64,7 @@ func evalAlternate(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
leftTerm := opTerm.Children[0] leftTerm := opTerm.Children[0]
if leftTerm.Tk.Sym != scan.SymVariable { if leftTerm.Tk.Sym != sym.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
@@ -99,7 +100,7 @@ func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err erro
} }
leftTerm := opTerm.Children[0] leftTerm := opTerm.Children[0]
if leftTerm.Tk.Sym != scan.SymVariable { if leftTerm.Tk.Sym != sym.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
@@ -123,7 +124,7 @@ func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err erro
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoubleQuestion, newDefaultTerm) scan.RegisterTermConstructor(sym.SymDoubleQuestion, newDefaultTerm)
scan.RegisterTermConstructor(scan.SymQuestionEqual, newDefaultAssignTerm) scan.RegisterTermConstructor(sym.SymQuestionEqual, newDefaultAssignTerm)
scan.RegisterTermConstructor(scan.SymQuestionExclam, newAlternateTerm) scan.RegisterTermConstructor(sym.SymQuestionExclam, newAlternateTerm)
} }
+4 -2
View File
@@ -10,6 +10,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- digest term //-------- digest term
@@ -48,9 +49,10 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
ctx.SetVar("#", it.Count()) ctx.SetVar("#", it.Count())
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil { if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
if rightValue == nil { if rightValue == nil {
break err = io.EOF
} else { } else {
lastValue = rightValue lastValue = rightValue
ctx.SetVar(kern.ControlLastResult, lastValue)
} }
} }
ctx.DeleteVar("#") ctx.DeleteVar("#")
@@ -69,5 +71,5 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwDigest, newDigestTerm) scan.RegisterTermConstructor(sym.SymKwDigest, newDigestTerm)
} }
+7 -20
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
) )
@@ -35,7 +37,7 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case kern.ExtIterator: case kern.ExtIterator:
if indexTerm.Symbol() == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ { if indexTerm.Symbol() == sym.SymVariable /*|| indexTerm.Tk.Sym == sym.SymString */ {
opName := indexTerm.Source() opName := indexTerm.Source()
if unboxedValue.HasOperation(opName) { if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, map[string]any{}) v, err = unboxedValue.CallOperation(opName, map[string]any{})
@@ -46,22 +48,7 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} else { } else {
err = indexTerm.Tk.ErrorExpectedGot("identifier") err = indexTerm.Tk.ErrorExpectedGot("identifier")
} }
case *kern.DictType: case *dict.DictType:
// var ok bool
// s := opTerm.Children[1].Symbol()
// if s == scan.SymVariable || s == scan.SymString {
// src := opTerm.Children[1].Source()
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
// src = src[1 : len(src)-1]
// }
// if v, ok = unboxedValue.GetItem(src); !ok {
// err = opTerm.Errorf("key %q not found", src)
// }
// } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
// if v, ok = unboxedValue.GetItem(rightValue); !ok {
// err = opTerm.Errorf("key %q not found", rightValue)
// }
// }
v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1]) v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1])
default: default:
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil { if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
@@ -71,11 +58,11 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
func dotGetDictItemValue(ctx kern.ExprContext, d *kern.DictType, rightTerm *scan.Term) (v any, err error) { func dotGetDictItemValue(ctx kern.ExprContext, d *dict.DictType, rightTerm *scan.Term) (v any, err error) {
var ok bool var ok bool
var rightValue any var rightValue any
s := rightTerm.Symbol() s := rightTerm.Symbol()
if s == scan.SymVariable || s == scan.SymString { if s == sym.SymVariable || s == sym.SymString {
// src := rightTerm.Source() // src := rightTerm.Source()
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' { // if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
// src = src[1 : len(src)-1] // src = src[1 : len(src)-1]
@@ -101,5 +88,5 @@ func errDictKeyNotFound(term *scan.Term, key any) error {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDot, newDotTerm) scan.RegisterTermConstructor(sym.SymDot, newDotTerm)
} }
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
) )
//-------- fact term //-------- fact term
@@ -30,7 +32,7 @@ func evalFact(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsInteger(leftValue) { if types.IsInteger(leftValue) {
if i, _ := leftValue.(int64); i >= 0 { if i, _ := leftValue.(int64); i >= 0 {
f := int64(1) f := int64(1)
for k := int64(1); k <= i; k++ { for k := int64(1); k <= i; k++ {
@@ -48,5 +50,5 @@ func evalFact(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymExclamation, newFactTerm) scan.RegisterTermConstructor(sym.SymExclamation, newFactTerm)
} }
+4 -2
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/boolean"
) )
//-------- map term //-------- map term
@@ -117,7 +119,7 @@ func (it *filterIterator) Next() (item any, err error) {
ctx.DeleteVar("_") ctx.DeleteVar("_")
if err == nil { if err == nil {
if success, valid := kern.ToBool(result); valid { if success, valid := boolean.ToBool(result); valid {
if success { if success {
item = attempt item = attempt
break break
@@ -163,5 +165,5 @@ func (it *filterIterator) CallOperation(name string, args map[string]any) (v any
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwFilter, newFilterTerm) scan.RegisterTermConstructor(sym.SymKwFilter, newFilterTerm)
} }
+6 -4
View File
@@ -11,6 +11,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/fract"
) )
// -------- fraction term // -------- fraction term
@@ -51,7 +53,7 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
num = -num num = -num
} }
if num != 0 { if num != 0 {
if g := kern.Gcd(num, den); g != 1 { if g := fract.Gcd(num, den); g != 1 {
num = num / g num = num / g
den = den / g den = den / g
} }
@@ -59,11 +61,11 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
v = num v = num
} else { } else {
// v = &expr.FractionType{num, den} // v = &expr.FractionType{num, den}
v = kern.NewFraction(num, den) v = fract.NewFraction(num, den)
} }
} else { } else {
// v = &FractionType{0, den} // v = &FractionType{0, den}
v = kern.NewFraction(0, den) v = fract.NewFraction(0, den)
} }
return return
} }
@@ -71,5 +73,5 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
// registerTermConstructor(SymVertBar, newFractionTerm) // registerTermConstructor(SymVertBar, newFractionTerm)
scan.RegisterTermConstructor(scan.SymColon, newFractionTerm) scan.RegisterTermConstructor(sym.SymColon, newFractionTerm)
} }
+12 -8
View File
@@ -11,6 +11,10 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- group by term //-------- group by term
@@ -48,17 +52,17 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
rightTk := opTerm.Children[1].Tk rightTk := opTerm.Children[1].Tk
if rightTk.IsSymbol(scan.SymVariable) && rightTk.Source() == "__" { if rightTk.IsSymbol(sym.SymVariable) && rightTk.Source() == "__" {
keyByIndex = true keyByIndex = true
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil { } else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
return return
} else if kern.IsString(rightValue) { } else if str.IsString(rightValue) {
sKey = rightValue.(string) sKey = rightValue.(string)
} else { } else {
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue)) return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
} }
values := kern.MakeDict() values := dict.MakeDict()
for item, err = it.Next(); err == nil; item, err = it.Next() { for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item) ctx.SetVar("_", item)
ctx.SetVar("__", it.Index()) ctx.SetVar("__", it.Index())
@@ -66,7 +70,7 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var sItemKey string var sItemKey string
if d, ok := item.(*kern.DictType); ok { if d, ok := item.(*dict.DictType); ok {
if keyByIndex || len(sKey) == 0 { if keyByIndex || len(sKey) == 0 {
sItemKey = strconv.Itoa(int(it.Index())) sItemKey = strconv.Itoa(int(it.Index()))
} else if d.HasKey(sKey) { } else if d.HasKey(sKey) {
@@ -86,12 +90,12 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
sItemKey = strconv.Itoa(int(it.Index())) sItemKey = strconv.Itoa(int(it.Index()))
} }
var ls *kern.ListType var ls *array.ArrayType
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil { if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
ls = lsAny.(*kern.ListType) ls = lsAny.(*array.ArrayType)
} }
if ls == nil { if ls == nil {
ls = kern.NewListA() ls = array.NewArrayA()
} }
ls.AppendItem(item) ls.AppendItem(item)
values.SetItem(sItemKey, ls) values.SetItem(sItemKey, ls)
@@ -109,5 +113,5 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwGroupBy, newGroupByTerm) scan.RegisterTermConstructor(sym.SymKwGroupBy, newGroupByTerm)
} }
+13 -6
View File
@@ -7,6 +7,10 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
//-------- in term //-------- in term
@@ -33,12 +37,15 @@ func evalIn(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsList(rightValue) { if array.IsArray(rightValue) {
list, _ := rightValue.(*kern.ListType) a, _ := rightValue.(*array.ArrayType)
v = list.IndexDeepSameCmp(leftValue) >= 0 v = a.IndexDeepSameCmp(leftValue) >= 0
} else if kern.IsDict(rightValue) { } else if dict.IsDict(rightValue) {
dict, _ := rightValue.(*kern.DictType) dict, _ := rightValue.(*dict.DictType)
v = dict.HasKey(leftValue) v = dict.HasKey(leftValue)
} else if list.IsLinkedList(rightValue) {
ls, _ := rightValue.(*list.LinkedList)
v = ls.HasValue(leftValue)
} else { } else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
@@ -47,5 +54,5 @@ func evalIn(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwIn, newInTerm) scan.RegisterTermConstructor(sym.SymKwIn, newInTerm)
} }
+8 -5
View File
@@ -7,6 +7,9 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- include term //-------- include term
@@ -29,9 +32,9 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
count := 0 count := 0
if kern.IsList(childValue) { if array.IsArray(childValue) {
list, _ := childValue.(*kern.ListType) a, _ := childValue.(*array.ArrayType)
for i, filePathSpec := range *list { for i, filePathSpec := range *a {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
@@ -44,7 +47,7 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
break break
} }
} }
} else if kern.IsString(childValue) { } else if str.IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
@@ -61,5 +64,5 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwInclude, newIncludeTerm) scan.RegisterTermConstructor(sym.SymKwInclude, newIncludeTerm)
} }
+163 -75
View File
@@ -5,8 +5,16 @@
package expr package expr
import ( import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/interval"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
// -------- index term // -------- index term
@@ -20,15 +28,15 @@ func newIndexTerm(tk *scan.Token) (inst *scan.Term) {
} }
} }
func verifyKey(indexList *kern.ListType) (index any, err error) { func verifyKey(indexList *array.ArrayType) (index any, err error) {
index = (*indexList)[0] index = (*indexList)[0]
return return
} }
func verifyIndex(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (index int, err error) { func verifyIndex(indexTerm kern.Term, indexList *array.ArrayType, maxValue int64) (index int64, err error) {
var v int var v int64
if v, err = kern.ToGoInt((*indexList)[0], "index expression"); err == nil { if v, err = types.ToGoInt64((*indexList)[0], "index expression"); err == nil {
if v < 0 && v >= -maxValue { if v < 0 && v >= -maxValue {
v = maxValue + v v = maxValue + v
} }
@@ -41,32 +49,32 @@ func verifyIndex(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (
return return
} }
func verifyRange(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (startIndex, endIndex int, err error) { // func verifyInterval(indexTerm kern.Term, indexList *array.ArrayType, maxValue int64) (startIndex, endIndex int64, err error) {
v, _ := ((*indexList)[0]).(*intPair) // v, _ := ((*indexList)[0]).(*interval.IntervalType)
startIndex = v.a // startIndex = v.Begin()
endIndex = v.b // endIndex = v.End()
if endIndex == kern.ConstLastIndex { // if endIndex == kern.ConstLastIndex {
endIndex = maxValue // endIndex = int64(maxValue)
} // }
if startIndex < 0 && startIndex >= -maxValue { // if startIndex < 0 && startIndex >= -maxValue {
startIndex = maxValue + startIndex // startIndex = int64(maxValue) + startIndex
} // }
if endIndex < 0 && endIndex >= -maxValue { // if endIndex < 0 && endIndex >= -maxValue {
endIndex = maxValue + endIndex // endIndex = int64(maxValue) + endIndex
} // }
if startIndex < 0 || startIndex > maxValue { // if startIndex < 0 || startIndex > maxValue {
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex) // err = indexTerm.Errorf("%s start-index %d is out of bounds", interval.TypeName, startIndex)
} else if endIndex < 0 || endIndex > maxValue { // } else if endIndex < 0 || endIndex > maxValue {
err = indexTerm.Errorf("range end-index %d is out of bounds", endIndex) // err = indexTerm.Errorf("%s end-index %d is out of bounds", interval.TypeName, endIndex)
} else if startIndex > endIndex { // } else if startIndex > endIndex {
err = indexTerm.Errorf("range start-index %d must not be greater than end-index %d", startIndex, endIndex) // err = indexTerm.Errorf("%s start-index %d must not be greater than end-index %d", interval.TypeName, startIndex, endIndex)
} // }
return // return
} // }
func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var indexList *kern.ListType var indexList *array.ArrayType
var ok bool var ok bool
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
@@ -74,7 +82,7 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
indexTerm := opTerm.Children[1] indexTerm := opTerm.Children[1]
if indexList, ok = rightValue.(*kern.ListType); !ok { if indexList, ok = rightValue.(*array.ArrayType); !ok {
err = opTerm.Errorf("invalid index expression") err = opTerm.Errorf("invalid index expression")
return return
} else if len(*indexList) != 1 { } else if len(*indexList) != 1 {
@@ -82,51 +90,12 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsInteger((*indexList)[0]) { if types.IsInteger((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { v, err = collectionItemByIndex(opTerm, leftValue, rightValue, indexList)
case *kern.ListType: } else if interval.IsInterval((*indexList)[0]) {
var index int v, err = subCollectionByIndex(opTerm, leftValue, rightValue, indexList)
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil { } else if dict.IsDict(leftValue) {
v = (*unboxedValue)[index] d := leftValue.(*dict.DictType)
}
case *kern.LinkedList:
var index int
if index, err = verifyIndex(indexTerm, indexList, unboxedValue.Len()); err == nil {
v, err = unboxedValue.At(index)
}
case string:
var index int
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
v = string(unboxedValue[index])
}
case *kern.DictType:
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default:
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
} else if isIntPair((*indexList)[0]) {
switch unboxedValue := leftValue.(type) {
case *kern.ListType:
var start, end int
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
sublist := kern.ListType((*unboxedValue)[start:end])
v = &sublist
}
case *kern.LinkedList:
var start, end int
if start, end, err = verifyRange(indexTerm, indexList, unboxedValue.Len()); err == nil {
v = unboxedValue.Sub(start, end)
}
case string:
var start, end int
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
v = unboxedValue[start:end]
}
default:
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
} else if kern.IsDict(leftValue) {
d := leftValue.(*kern.DictType)
v, err = getDictItem(d, indexTerm, indexList, rightValue) v, err = getDictItem(d, indexTerm, indexList, rightValue)
} else { } else {
rightChild := opTerm.Children[1] rightChild := opTerm.Children[1]
@@ -135,7 +104,126 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
func getDictItem(d *kern.DictType, indexTerm *scan.Term, indexList *kern.ListType, rightValue any) (v any, err error) { func collectionItemByIndex(opTerm kern.Term, leftValue, rightValue any, indexList *array.ArrayType) (v any, err error) {
indexTerm := opTerm.GetChild(1)
switch unboxedValue := leftValue.(type) {
case *array.ArrayType:
var index int64
if index, err = verifyIndex(indexTerm, indexList, int64(len(*unboxedValue))); err == nil {
v = (*unboxedValue)[index]
}
case *list.LinkedList:
var index int64
if index, err = verifyIndex(indexTerm, indexList, int64(unboxedValue.Len())); err == nil {
v, err = unboxedValue.At(index)
}
case string:
var index int64
if index, err = verifyIndex(indexTerm, indexList, int64(len(unboxedValue))); err == nil {
v = string(unboxedValue[index])
}
case *dict.DictType:
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default:
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
return
}
func subCollectionByIndex(opTerm kern.Term, leftValue, rightValue any, indexList *array.ArrayType) (v any, err error) {
triple := (*indexList)[0].(*interval.IntervalType)
switch unboxedValue := leftValue.(type) {
case *array.ArrayType:
v = intervalSubArray(unboxedValue, triple)
case *list.LinkedList:
v = intervalSubLinkedList(unboxedValue, triple)
case string:
v = intervalSubString(unboxedValue, triple)
default:
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
return
}
func fixInterval(p *interval.IntervalType, maxLength int) (b, e, s int) {
b, e, s = p.ToIntTriple()
if b < 0 {
b = maxLength + b
}
if e < 0 {
e = maxLength + e //+ 1
}
return
}
func intervalSubString(source string, p *interval.IntervalType) (result string) {
b, e, s := fixInterval(p, len(source))
if b <= e {
e = min(e, len(source))
if s == 1 {
result = source[b:e]
} else {
var sb strings.Builder
for i := b; i < e; i += s {
sb.WriteByte(source[i])
}
result = sb.String()
}
} else {
var sb strings.Builder
for i := b; i >= e; i -= s {
sb.WriteByte(source[i])
}
result = sb.String()
}
return
}
func intervalSubArray(source *array.ArrayType, p *interval.IntervalType) (result *array.ArrayType) {
var a array.ArrayType
b, e, s := fixInterval(p, len(*source))
if b <= e {
e = min(e, len(*source))
if s == 1 {
a = (*source)[b:e]
} else {
size := (e-b)/s + 1
a = make(array.ArrayType, 0, size)
for i := b; i < e; i += s {
a = append(a, (*source)[i])
}
}
} else {
size := (b-e)/s + 1
a = make(array.ArrayType, 0, size)
for i := b; i > e; i -= s {
a = append(a, (*source)[i])
}
}
result = &a
return
}
func intervalSubLinkedList(source *list.LinkedList, p *interval.IntervalType) (result *list.LinkedList) {
b, e, s := fixInterval(p, source.Len())
revert := b > e
if revert {
b, e = e, b
source = source.Revert()
}
e = min(e, source.Len())
if s == 1 {
result = source.Sub(int64(b), int64(e))
} else {
result = source.SubStep(int64(b), int64(e), int64(s))
}
if revert {
source = source.Revert()
}
return
}
func getDictItem(d *dict.DictType, indexTerm kern.Term, indexList *array.ArrayType, rightValue any) (v any, err error) {
var ok bool var ok bool
var indexValue any var indexValue any
@@ -149,5 +237,5 @@ func getDictItem(d *kern.DictType, indexTerm *scan.Term, indexList *kern.ListTyp
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymIndex, newIndexTerm) scan.RegisterTermConstructor(sym.SymIndex, newIndexTerm)
} }
+37 -44
View File
@@ -7,6 +7,9 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
//-------- prepend term //-------- prepend term
@@ -31,34 +34,38 @@ func newAppendTerm(tk *scan.Token) (inst *scan.Term) {
} }
} }
func prependToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) { func prependToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) {
if list, ok := rightValue.(*kern.ListType); ok { if a, ok := rightValue.(*array.ArrayType); ok {
var it kern.Iterator var it kern.Iterator
if it, ok = leftValue.(kern.Iterator); !ok { if it, ok = leftValue.(kern.Iterator); !ok {
it = NewFormalIterator(leftValue) it = NewFormalIterator(leftValue)
} }
ls := kern.NewLinkedList() ls := list.NewLinkedList()
ls.SeqPushBack(it) ls.SeqPushBack(it)
newList := kern.ListType(nil) newArray := array.ArrayType(nil)
if newSize := len(*list) + int(ls.Len()); newSize > cap(*list) { if newSize := len(*a) + int(ls.Len()); newSize > cap(*a) {
newList = make([]any, 0, newSize) newArray = make([]any, 0, newSize)
for node := ls.FirstNode(); node != nil; node = node.Next() { for node := ls.FirstNode(); node != nil; node = node.Next() {
newList = append(newList, node.Data()) newArray = append(newArray, node.Data())
} }
} }
for _, item := range *list { for _, item := range *a {
newList = append(newList, item) newArray = append(newArray, item)
} }
result = &newList if opTerm.GetRightChild().IsVar() {
} else if list, ok := rightValue.(*kern.LinkedList); ok { // Update the original array if the right operand is a variable
*a = newArray
}
result = &newArray
} else if ll, ok := rightValue.(*list.LinkedList); ok {
var it kern.Iterator var it kern.Iterator
if it, ok = leftValue.(kern.Iterator); !ok { if it, ok = leftValue.(kern.Iterator); !ok {
it = NewFormalIterator(leftValue) it = NewFormalIterator(leftValue)
} }
result = list.SeqPushBack(it) result = ll.SeqPushBack(it)
} else { } else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
@@ -76,34 +83,38 @@ func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
func appendToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) { func appendToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) {
if list, ok := leftValue.(*kern.ListType); ok { if a, ok := leftValue.(*array.ArrayType); ok {
var it kern.Iterator var it kern.Iterator
if it, ok = rightValue.(kern.Iterator); !ok { if it, ok = rightValue.(kern.Iterator); !ok {
it = NewFormalIterator(rightValue) it = NewFormalIterator(rightValue)
} }
ls := kern.NewLinkedList() ls := list.NewLinkedList()
ls.SeqPushBack(it) ls.SeqPushBack(it)
newList := *list newArray := *a
if newSize := len(*list) + int(ls.Len()); newSize > cap(*list) { if newSize := len(*a) + int(ls.Len()); newSize > cap(*a) {
newList = make([]any, 0, newSize) newArray = make([]any, 0, newSize)
for _, item := range *list { for _, item := range *a {
newList = append(newList, item) newArray = append(newArray, item)
} }
} }
for node := ls.FirstNode(); node != nil; node = node.Next() { for node := ls.FirstNode(); node != nil; node = node.Next() {
newList = append(newList, node.Data()) newArray = append(newArray, node.Data())
} }
result = &newList if opTerm.GetLeftChild().IsVar() {
} else if list, ok := leftValue.(*kern.LinkedList); ok { // Update the original array if the left operand is a variable
*a = newArray
}
result = &newArray
} else if ll, ok := leftValue.(*list.LinkedList); ok {
var it kern.Iterator var it kern.Iterator
if it, ok = rightValue.(kern.Iterator); !ok { if it, ok = rightValue.(kern.Iterator); !ok {
it = NewFormalIterator(rightValue) it = NewFormalIterator(rightValue)
} }
result = list.SeqPushBack(it) result = ll.SeqPushBack(it)
} else { } else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
@@ -122,26 +133,8 @@ func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
// var leftValue, rightValue any
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
// return
// }
// if IsList(leftValue) {
// list, _ := leftValue.(*ListType)
// newList := append(*list, rightValue)
// v = &newList
// if
// } else {
// err = self.errIncompatibleTypes(leftValue, rightValue)
// }
// return
// }
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymPlusGreater, newPrependTerm) scan.RegisterTermConstructor(sym.SymPlusGreater, newPrependTerm)
scan.RegisterTermConstructor(scan.SymLessPlus, newAppendTerm) scan.RegisterTermConstructor(sym.SymLessPlus, newAppendTerm)
} }
+103
View File
@@ -0,0 +1,103 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-interval.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/interval"
)
// -------- interval term
func newIntervalTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriInterval,
EvalFunc: evalInterval,
}
}
func changeColonToInterval(t *scan.Term) {
if t.IsSymbol(sym.SymDoubleDot) {
t.SetSymbol(sym.SymInterval)
t.EvalFunc = evalInterval
if child := t.GetLeftChild(); child != nil && child.IsSymbol(sym.SymDoubleDot) {
child.SetSymbol(sym.SymInterval)
}
}
}
func evalInterval(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var begin, end int64
step := int64(1)
switch opTerm.GetChildCount() {
case 0:
begin = 0
end = int64(kern.ConstLastIndex)
case 1:
var leftValue any
if leftValue, err = opTerm.GetChild(0).Compute(ctx); err != nil {
return
}
if begin, err = checkIntervalComponent(opTerm, leftValue); err != nil {
return
}
end = int64(kern.ConstLastIndex)
default:
var beginValue, endValue, stepValue any
subTerm := opTerm.GetLeftChild()
if subTerm.IsOneOf(sym.SymInterval, sym.SymDoubleDot) {
if beginValue, endValue, err = subTerm.EvalInfix(ctx); err != nil {
return
}
if stepValue, err = opTerm.GetRightChild().Compute(ctx); err != nil {
return
}
step, err = checkIntervalComponent(opTerm.GetRightChild(), stepValue)
} else if beginValue, endValue, err = opTerm.EvalInfix(ctx); err != nil {
return
}
if begin, err = checkIntervalComponent(opTerm, beginValue); err != nil {
return
}
if end, err = checkIntervalComponent(opTerm, endValue); err != nil {
return
}
}
if step <= 0 {
err = opTerm.Errorf("invalid %s specification: step must be positive", interval.TypeName)
} else {
v = interval.NewInterval(begin, end, step)
}
return
}
func checkIntervalComponent(t kern.Term, value any) (component int64, err error) {
if component, err = types.ToGoInt64(value, "%s expression"); err != nil {
err = t.Errorf(err.Error(), interval.TypeName)
}
return
}
// func errIntervalInvalidSpecification(t kern.Term) error {
// return t.Errorf("invalid %s specification", interval.TypeName)
// }
func errIntervalUnexpectedExpression(t *scan.Term) error {
return t.Errorf("unexpected %s expression", interval.TypeName)
}
// init
func init() {
scan.RegisterTermConstructor(sym.SymInterval, newIntervalTerm)
scan.RegisterTermConstructor(sym.SymDoubleDot, newIntervalTerm)
}
+2 -1
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- iter value term //-------- iter value term
@@ -39,5 +40,5 @@ func evalIterValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm) // registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
scan.RegisterTermConstructor(scan.SymDereference, newIterValueTerm) scan.RegisterTermConstructor(sym.SymDereference, newIterValueTerm)
} }
+12 -13
View File
@@ -7,6 +7,11 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- length term //-------- length term
@@ -28,25 +33,19 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsList(childValue) { if array.IsArray(childValue) {
ls, _ := childValue.(*kern.ListType) ls, _ := childValue.(*array.ArrayType)
v = int64(len(*ls)) v = int64(len(*ls))
} else if kern.IsString(childValue) { } else if str.IsString(childValue) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if kern.IsDict(childValue) { } else if dict.IsDict(childValue) {
m, _ := childValue.(*kern.DictType) m, _ := childValue.(*dict.DictType)
v = int64(len(*m)) v = int64(len(*m))
} else if lls, ok := childValue.(*kern.LinkedList); ok { } else if lls, ok := childValue.(*list.LinkedList); ok {
v = int64(lls.Len()) v = int64(lls.Len())
} else if it, ok := childValue.(kern.Iterator); ok { } else if it, ok := childValue.(kern.Iterator); ok {
v = int64(it.Count()) v = int64(it.Count())
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
// count, _ := extIt.CallOperation(CountName, nil)
// v, _ = ToGoInt(count, "")
// } else {
// v = int64(it.Index() + 1)
// }
} else { } else {
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
@@ -55,5 +54,5 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymHash, newLengthTerm) scan.RegisterTermConstructor(sym.SymHash, newLengthTerm)
} }
+2 -22
View File
@@ -9,6 +9,7 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- map term //-------- map term
@@ -44,32 +45,11 @@ func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
} }
// values := kern.NewListA()
// for item, err = it.Next(); err == nil; item, err = it.Next() {
// ctx.SetVar("_", item)
// ctx.SetVar("__", it.Index())
// ctx.SetVar("_#", it.Count())
// if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
// values.AppendItem(rightValue)
// }
// ctx.DeleteVar("_#")
// ctx.DeleteVar("__")
// ctx.DeleteVar("_")
// if err != nil {
// break
// }
// }
// if err == io.EOF {
// err = nil
// }
// if err == nil {
// v = values
// }
v, err = NewIterIter(it, ctx, opTerm.Children[1:]) v, err = NewIterIter(it, ctx, opTerm.Children[1:])
return return
} }
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwMap, newMapTerm) scan.RegisterTermConstructor(sym.SymKwMap, newMapTerm)
} }
+2 -1
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- plugin term //-------- plugin term
@@ -37,5 +38,5 @@ func evalPlugin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwPlugin, newPluginTerm) scan.RegisterTermConstructor(sym.SymKwPlugin, newPluginTerm)
} }
+7 -5
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
) )
// -------- post increment term // -------- post increment term
@@ -31,7 +33,7 @@ func evalPostInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var namePrefix string var namePrefix string
v, err = it.Next() v, err = it.Next()
if opTerm.Children[0].Symbol() == scan.SymVariable { if opTerm.Children[0].Symbol() == sym.SymVariable {
namePrefix = opTerm.Children[0].Source() namePrefix = opTerm.Children[0].Source()
} }
ctx.UnsafeSetVar(namePrefix+"_index", it.Index()) ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
@@ -49,7 +51,7 @@ func evalPostInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
ctx.UnsafeSetVar(namePrefix+"_value", v1) ctx.UnsafeSetVar(namePrefix+"_value", v1)
} }
} }
} else if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable { } else if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(opTerm.Children[0].Source(), i+1) ctx.SetVar(opTerm.Children[0].Source(), i+1)
@@ -79,7 +81,7 @@ func evalPostDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
/* if it, ok := childValue.(Iterator); ok { /* if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else */if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable { } else */if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(opTerm.Children[0].Source(), i-1) ctx.SetVar(opTerm.Children[0].Source(), i-1)
@@ -91,6 +93,6 @@ func evalPostDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoublePlus, newPostIncTerm) scan.RegisterTermConstructor(sym.SymDoublePlus, newPostIncTerm)
scan.RegisterTermConstructor(scan.SymDoubleMinus, newPostDecTerm) scan.RegisterTermConstructor(sym.SymDoubleMinus, newPostDecTerm)
} }
+6 -4
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
) )
// -------- pre increment term // -------- pre increment term
@@ -27,7 +29,7 @@ func evalPreInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable { if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
i := childValue.(int64) + 1 i := childValue.(int64) + 1
ctx.SetVar(opTerm.Children[0].Source(), i) ctx.SetVar(opTerm.Children[0].Source(), i)
v = i v = i
@@ -56,7 +58,7 @@ func evalPreDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable { if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
i := childValue.(int64) - 1 i := childValue.(int64) - 1
ctx.SetVar(opTerm.Children[0].Source(), i) ctx.SetVar(opTerm.Children[0].Source(), i)
v = i v = i
@@ -68,6 +70,6 @@ func evalPreDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymPreInc, newPreIncTerm) scan.RegisterTermConstructor(sym.SymPreInc, newPreIncTerm)
scan.RegisterTermConstructor(scan.SymPreDec, newPreDecTerm) scan.RegisterTermConstructor(sym.SymPreDec, newPreDecTerm)
} }
+30 -24
View File
@@ -9,6 +9,12 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- multiply term //-------- multiply term
@@ -24,15 +30,15 @@ func newMultiplyTerm(tk *scan.Token) (inst *scan.Term) {
} }
func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsString(leftValue) && kern.IsInteger(rightValue) { if str.IsString(leftValue) && types.IsInteger(rightValue) {
s, _ := leftValue.(string) s, _ := leftValue.(string)
n, _ := rightValue.(int64) n, _ := rightValue.(int64)
v = strings.Repeat(s, int(n)) v = strings.Repeat(s, int(n))
} else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { } else if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue) v = types.NumAsFloat(leftValue) * types.NumAsFloat(rightValue)
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) { } else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
v, err = kern.MulAnyFract(leftValue, rightValue) v, err = fract.MulAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
@@ -67,16 +73,16 @@ func newDivideTerm(tk *scan.Token) (inst *scan.Term) {
} }
func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
d := kern.NumAsFloat(rightValue) d := types.NumAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = opTerm.ErrDivisionByZero() err = opTerm.ErrDivisionByZero()
} else { } else {
v = kern.NumAsFloat(leftValue) / d v = types.NumAsFloat(leftValue) / d
} }
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) { } else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
v, err = kern.DivAnyFract(leftValue, rightValue) v, err = fract.DivAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
if rightInt, _ := rightValue.(int64); rightInt == 0 { if rightInt, _ := rightValue.(int64); rightInt == 0 {
@@ -85,11 +91,11 @@ func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error)
v = leftInt / rightInt v = leftInt / rightInt
} }
} }
} else if kern.IsString(leftValue) && kern.IsString(rightValue) { } else if str.IsString(leftValue) && str.IsString(rightValue) {
source := leftValue.(string) source := leftValue.(string)
sep := rightValue.(string) sep := rightValue.(string)
v = kern.ListFromStrings(strings.Split(source, sep)) v = array.ArrayFromStrings(strings.Split(source, sep))
} else if kern.IsString(leftValue) && kern.IsInteger(rightValue) { } else if str.IsString(leftValue) && types.IsInteger(rightValue) {
source := leftValue.(string) source := leftValue.(string)
partSize := int(rightValue.(int64)) partSize := int(rightValue.(int64))
if partSize == 0 { if partSize == 0 {
@@ -108,7 +114,7 @@ func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error)
if remainder > 0 { if remainder > 0 {
parts = append(parts, source[len(source)-remainder:]) parts = append(parts, source[len(source)-remainder:])
} }
v = kern.NewList(parts) v = array.NewArray(parts)
} }
} else { } else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
@@ -145,12 +151,12 @@ func evalDivideAsFloat(ctx kern.ExprContext, floatDivTerm *scan.Term) (v any, er
return return
} }
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
d := kern.NumAsFloat(rightValue) d := types.NumAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = floatDivTerm.ErrDivisionByZero() err = floatDivTerm.ErrDivisionByZero()
} else { } else {
v = kern.NumAsFloat(leftValue) / d v = types.NumAsFloat(leftValue) / d
} }
} else { } else {
err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue) err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue)
@@ -170,7 +176,7 @@ func newRemainderTerm(tk *scan.Token) (inst *scan.Term) {
} }
} }
func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) { if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
if rightInt == 0 { if rightInt == 0 {
err = opTerm.ErrDivisionByZero() err = opTerm.ErrDivisionByZero()
@@ -196,8 +202,8 @@ func evalRemainder(ctx kern.ExprContext, remainderTerm *scan.Term) (v any, err e
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymStar, newMultiplyTerm) scan.RegisterTermConstructor(sym.SymStar, newMultiplyTerm)
scan.RegisterTermConstructor(scan.SymSlash, newDivideTerm) scan.RegisterTermConstructor(sym.SymSlash, newDivideTerm)
scan.RegisterTermConstructor(scan.SymDotSlash, newDivideAsFloatTerm) scan.RegisterTermConstructor(sym.SymDotSlash, newDivideAsFloatTerm)
scan.RegisterTermConstructor(scan.SymPercent, newRemainderTerm) scan.RegisterTermConstructor(sym.SymPercent, newRemainderTerm)
} }
-87
View File
@@ -1,87 +0,0 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-range.go
package expr
import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- range term
type intPair struct {
a, b int
}
func (p *intPair) TypeName() string {
return kern.TypePair
}
func (p *intPair) ToString(opt kern.FmtOpt) string {
return fmt.Sprintf("(%d, %d)", p.a, p.b)
}
func isIntPair(v any) bool {
_, ok := v.(*intPair)
return ok
}
func newRangeTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriRange,
EvalFunc: evalRange,
}
}
func changeColonToRange(t *scan.Term) {
if t.Tk.IsSymbol(scan.SymColon) {
t.Tk.Sym = scan.SymRange
t.EvalFunc = evalRange
}
}
func evalRange(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
if len(opTerm.Children) == 0 {
leftValue = int64(0)
rightValue = int64(-1)
} else if len(opTerm.Children) == 1 {
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
rightValue = int64(kern.ConstLastIndex)
} else if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return
}
if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = errRangeInvalidSpecification(opTerm)
return
}
startIndex, _ := leftValue.(int64)
endIndex, _ := rightValue.(int64)
v = &intPair{int(startIndex), int(endIndex)}
return
}
func errRangeInvalidSpecification(t *scan.Term) error {
return t.Errorf("invalid range specification")
}
func errRangeUnexpectedExpression(t *scan.Term) error {
return t.Errorf("unexpected range expression")
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymRange, newRangeTerm)
}
+30 -24
View File
@@ -9,6 +9,12 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/boolean"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- equal term //-------- equal term
@@ -26,22 +32,22 @@ func newEqualTerm(tk *scan.Token) (inst *scan.Term) {
// type deepFuncTemplate func(a, b any) (eq bool, err error) // type deepFuncTemplate func(a, b any) (eq bool, err error)
func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) { func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) { if types.IsNumOrFract(a) && types.IsNumOrFract(b) {
if kern.IsNumber(a) && kern.IsNumber(b) { if types.IsNumber(a) && types.IsNumber(b) {
if kern.IsInteger(a) && kern.IsInteger(b) { if types.IsInteger(a) && types.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
eq = li == ri eq = li == ri
} else { } else {
eq = kern.NumAsFloat(a) == kern.NumAsFloat(b) eq = types.NumAsFloat(a) == types.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = kern.CmpAnyFract(a, b); err == nil { if cmp, err = fract.CmpAnyFract(a, b); err == nil {
eq = cmp == 0 eq = cmp == 0
} }
} }
} else if deepCmp != nil && kern.IsList(a) && kern.IsList(b) { } else if deepCmp != nil && array.IsArray(a) && array.IsArray(b) {
eq, err = deepCmp(a, b) eq, err = deepCmp(a, b)
} else { } else {
eq = reflect.DeepEqual(a, b) eq = reflect.DeepEqual(a, b)
@@ -75,7 +81,7 @@ func newNotEqualTerm(tk *scan.Token) (inst *scan.Term) {
func evalNotEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) { func evalNotEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if v, err = evalEqual(ctx, opTerm); err == nil { if v, err = evalEqual(ctx, opTerm); err == nil {
b, _ := kern.ToBool(v) b, _ := boolean.ToBool(v)
v = !b v = !b
} }
return return
@@ -94,29 +100,29 @@ func newLessTerm(tk *scan.Token) (inst *scan.Term) {
} }
func lessThan(self *scan.Term, a, b any) (isLess bool, err error) { func lessThan(self *scan.Term, a, b any) (isLess bool, err error) {
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) { if types.IsNumOrFract(a) && types.IsNumOrFract(b) {
if kern.IsNumber(a) && kern.IsNumber(b) { if types.IsNumber(a) && types.IsNumber(b) {
if kern.IsInteger(a) && kern.IsInteger(b) { if types.IsInteger(a) && types.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
isLess = li < ri isLess = li < ri
} else { } else {
isLess = kern.NumAsFloat(a) < kern.NumAsFloat(b) isLess = types.NumAsFloat(a) < types.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = kern.CmpAnyFract(a, b); err == nil { if cmp, err = fract.CmpAnyFract(a, b); err == nil {
isLess = cmp < 0 isLess = cmp < 0
} }
} }
} else if kern.IsString(a) && kern.IsString(b) { } else if str.IsString(a) && str.IsString(b) {
ls, _ := a.(string) ls, _ := a.(string)
rs, _ := b.(string) rs, _ := b.(string)
isLess = ls < rs isLess = ls < rs
// Inclusion test // Inclusion test
} else if kern.IsList(a) && kern.IsList(b) { } else if array.IsArray(a) && array.IsArray(b) {
aList, _ := a.(*kern.ListType) aList, _ := a.(*array.ArrayType)
bList, _ := b.(*kern.ListType) bList, _ := b.(*array.ArrayType)
isLess = len(*aList) < len(*bList) && bList.Contains(aList) isLess = len(*aList) < len(*bList) && bList.Contains(aList)
} else { } else {
err = self.ErrIncompatibleTypes(a, b) err = self.ErrIncompatibleTypes(a, b)
@@ -149,8 +155,8 @@ func newLessEqualTerm(tk *scan.Token) (inst *scan.Term) {
func lessThanOrEqual(self *scan.Term, a, b any) (isLessEq bool, err error) { func lessThanOrEqual(self *scan.Term, a, b any) (isLessEq bool, err error) {
if isLessEq, err = lessThan(self, a, b); err == nil { if isLessEq, err = lessThan(self, a, b); err == nil {
if !isLessEq { if !isLessEq {
if kern.IsList(a) && kern.IsList(b) { if array.IsArray(a) && array.IsArray(b) {
isLessEq, err = kern.SameContent(a, b) isLessEq, err = array.SameContent(a, b)
} else { } else {
isLessEq, err = equals(a, b, nil) isLessEq, err = equals(a, b, nil)
} }
@@ -219,10 +225,10 @@ func evalGreaterEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoubleEqual, newEqualTerm) scan.RegisterTermConstructor(sym.SymDoubleEqual, newEqualTerm)
scan.RegisterTermConstructor(scan.SymNotEqual, newNotEqualTerm) scan.RegisterTermConstructor(sym.SymNotEqual, newNotEqualTerm)
scan.RegisterTermConstructor(scan.SymLess, newLessTerm) scan.RegisterTermConstructor(sym.SymLess, newLessTerm)
scan.RegisterTermConstructor(scan.SymLessOrEqual, newLessEqualTerm) scan.RegisterTermConstructor(sym.SymLessOrEqual, newLessEqualTerm)
scan.RegisterTermConstructor(scan.SymGreater, newGreaterTerm) scan.RegisterTermConstructor(sym.SymGreater, newGreaterTerm)
scan.RegisterTermConstructor(scan.SymGreaterOrEqual, newGreaterEqualTerm) scan.RegisterTermConstructor(sym.SymGreaterOrEqual, newGreaterEqualTerm)
} }
+2 -1
View File
@@ -7,6 +7,7 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
//-------- selector term //-------- selector term
@@ -82,5 +83,5 @@ func evalSelector(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymSelector, newSelectorTerm) scan.RegisterTermConstructor(sym.SymSelector, newSelectorTerm)
} }
+6 -4
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
) )
//-------- bit right shift term //-------- bit right shift term
@@ -22,7 +24,7 @@ func newRightShiftTerm(tk *scan.Token) (inst *scan.Term) {
} }
func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) { if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
leftInt := leftValue.(int64) leftInt := leftValue.(int64)
rightInt := rightValue.(int64) rightInt := rightValue.(int64)
v = leftInt >> rightInt v = leftInt >> rightInt
@@ -56,7 +58,7 @@ func newLeftShiftTerm(tk *scan.Token) (inst *scan.Term) {
} }
func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) { if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
leftInt := leftValue.(int64) leftInt := leftValue.(int64)
rightInt := rightValue.(int64) rightInt := rightValue.(int64)
v = leftInt << rightInt v = leftInt << rightInt
@@ -81,6 +83,6 @@ func evalLeftShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymDoubleGreater, newRightShiftTerm) scan.RegisterTermConstructor(sym.SymDoubleGreater, newRightShiftTerm)
scan.RegisterTermConstructor(scan.SymDoubleLess, newLeftShiftTerm) scan.RegisterTermConstructor(sym.SymDoubleLess, newLeftShiftTerm)
} }
+9 -6
View File
@@ -7,6 +7,9 @@ package expr
import ( import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/float"
) )
//-------- plus sign term //-------- plus sign term
@@ -38,15 +41,15 @@ func evalSign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return return
} }
if kern.IsFloat(rightValue) { if float.IsFloat(rightValue) {
if opTerm.Tk.Sym == scan.SymChangeSign { if opTerm.Tk.Sym == sym.SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if kern.IsInteger(rightValue) { } else if types.IsInteger(rightValue) {
if opTerm.Tk.Sym == scan.SymChangeSign { if opTerm.Tk.Sym == sym.SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
} else { } else {
@@ -60,6 +63,6 @@ func evalSign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymUnchangeSign, newPlusSignTerm) scan.RegisterTermConstructor(sym.SymUnchangeSign, newPlusSignTerm)
scan.RegisterTermConstructor(scan.SymChangeSign, newMinusSignTerm) scan.RegisterTermConstructor(sym.SymChangeSign, newMinusSignTerm)
} }
+45 -30
View File
@@ -10,6 +10,14 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/float"
"git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/list"
"git.portale-stac.it/go-pkg/expr/types/str"
) )
//-------- plus term //-------- plus term
@@ -25,39 +33,46 @@ func newPlusTerm(tk *scan.Token) (inst *scan.Term) {
} }
func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if (kern.IsString(leftValue) && kern.IsNumberString(rightValue)) || (kern.IsString(rightValue) && kern.IsNumberString(leftValue)) { if (str.IsString(leftValue) && types.IsNumberString(rightValue)) || (str.IsString(rightValue) && types.IsNumberString(leftValue)) {
v = fmt.Sprintf("%v%v", leftValue, rightValue) v = fmt.Sprintf("%v%v", leftValue, rightValue)
} else if kern.IsNumber(leftValue) && kern.IsNumber(rightValue) { } else if types.IsNumber(leftValue) && types.IsNumber(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue) v = types.NumAsFloat(leftValue) + types.NumAsFloat(rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt + rightInt v = leftInt + rightInt
} }
} else if kern.IsList(leftValue) && kern.IsList(rightValue) { } else if array.IsArray(leftValue) && array.IsArray(rightValue) {
var leftList, rightList *kern.ListType var leftList, rightList *array.ArrayType
leftList, _ = leftValue.(*kern.ListType) leftList, _ = leftValue.(*array.ArrayType)
rightList, _ = rightValue.(*kern.ListType) rightList, _ = rightValue.(*array.ArrayType)
sumList := make(kern.ListType, 0, len(*leftList)+len(*rightList)) sumList := make(array.ArrayType, 0, len(*leftList)+len(*rightList))
sumList = append(sumList, *leftList...) sumList = append(sumList, *leftList...)
sumList = append(sumList, *rightList...) sumList = append(sumList, *rightList...)
v = &sumList v = &sumList
} else if (kern.IsFraction(leftValue) && kern.IsNumber(rightValue)) || (kern.IsFraction(rightValue) && kern.IsNumber(leftValue)) { } else if list.IsLinkedList(leftValue) && list.IsLinkedList(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { lsLeft := leftValue.(*list.LinkedList)
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue) lsRight := rightValue.(*list.LinkedList)
ls := lsLeft.Clone()
ls.SeqClonePushBack(NewLinkedListIterator(lsRight, nil))
v = ls
} else if (fract.IsFraction(leftValue) && types.IsNumber(rightValue)) || (fract.IsFraction(rightValue) && types.IsNumber(leftValue)) {
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
v = types.NumAsFloat(leftValue) + types.NumAsFloat(rightValue)
} else { } else {
v, err = kern.SumAnyFract(leftValue, rightValue) v, err = fract.SumAnyFract(leftValue, rightValue)
} }
} else if kern.IsDict(leftValue) && kern.IsDict(rightValue) { } else if dict.IsDict(leftValue) && dict.IsDict(rightValue) {
leftDict, _ := leftValue.(*kern.DictType) leftDict, _ := leftValue.(*dict.DictType)
rightDict, _ := rightValue.(*kern.DictType) rightDict, _ := rightValue.(*dict.DictType)
c := leftDict.Clone() clone := leftDict.Clone()
c := clone.(*dict.DictType)
c.Merge(rightDict) c.Merge(rightDict)
v = c v = c
} else if kern.IsFraction(leftValue) && kern.IsFraction(rightValue) { } else if fract.IsFraction(leftValue) && fract.IsFraction(rightValue) {
v, err = kern.SumAnyFract(leftValue, rightValue) v, err = fract.SumAnyFract(leftValue, rightValue)
} else { } else {
err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue) err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
@@ -87,20 +102,20 @@ func newMinusTerm(tk *scan.Token) (inst *scan.Term) {
} }
func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) { func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) { if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) { if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
v = kern.NumAsFloat(leftValue) - kern.NumAsFloat(rightValue) v = types.NumAsFloat(leftValue) - types.NumAsFloat(rightValue)
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) { } else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
v, err = kern.SubAnyFract(leftValue, rightValue) v, err = fract.SubAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt - rightInt v = leftInt - rightInt
} }
} else if kern.IsList(leftValue) && kern.IsList(rightValue) { } else if array.IsArray(leftValue) && array.IsArray(rightValue) {
leftList, _ := leftValue.(*kern.ListType) leftList, _ := leftValue.(*array.ArrayType)
rightList, _ := rightValue.(*kern.ListType) rightList, _ := rightValue.(*array.ArrayType)
diffList := make(kern.ListType, 0, len(*leftList)-len(*rightList)) diffList := make(array.ArrayType, 0, len(*leftList)-len(*rightList))
for _, item := range *leftList { for _, item := range *leftList {
if slices.Index(*rightList, item) < 0 { if slices.Index(*rightList, item) < 0 {
diffList = append(diffList, item) diffList = append(diffList, item)
@@ -125,6 +140,6 @@ func evalMinus(ctx kern.ExprContext, minusTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymPlus, newPlusTerm) scan.RegisterTermConstructor(sym.SymPlus, newPlusTerm)
scan.RegisterTermConstructor(scan.SymMinus, newMinusTerm) scan.RegisterTermConstructor(sym.SymMinus, newMinusTerm)
} }
+5 -3
View File
@@ -9,6 +9,8 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/array"
) )
//-------- unset term //-------- unset term
@@ -50,8 +52,8 @@ func evalUnset(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
} }
count := 0 count := 0
if kern.IsList(childValue) { if array.IsArray(childValue) {
list, _ := childValue.(*kern.ListType) list, _ := childValue.(*array.ArrayType)
for _, item := range *list { for _, item := range *list {
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil { if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
break break
@@ -70,5 +72,5 @@ func evalUnset(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// init // init
func init() { func init() {
scan.RegisterTermConstructor(scan.SymKwUnset, newUnsetTerm) scan.RegisterTermConstructor(sym.SymKwUnset, newUnsetTerm)
} }
+115 -112
View File
@@ -10,6 +10,7 @@ import (
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
) )
@@ -58,7 +59,7 @@ func NewParser() (p *parser) {
} }
func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) { func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
for tk = scanner.Next(); tk.IsSymbol(scan.SymComment); tk = scanner.Next() { for tk = scanner.Next(); tk.IsSymbol(sym.SymComment); tk = scanner.Next() {
} }
return return
} }
@@ -66,10 +67,10 @@ func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) { func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) {
args := make([]*scan.Term, 0, 10) args := make([]*scan.Term, 0, 10)
itemExpected := false itemExpected := false
lastSym := scan.SymUnknown lastSym := sym.SymUnknown
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos { for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
var subTree *scan.Ast var subTree *scan.Ast
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err != nil { if subTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.SymClosedRound); err != nil {
break break
} }
prev := scanner.Previous() prev := scanner.Previous()
@@ -80,11 +81,11 @@ func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk
break break
} }
itemExpected = prev.Sym == scan.SymComma itemExpected = prev.Sym == sym.SymComma
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
} }
if err == nil { if err == nil {
if lastSym != scan.SymClosedRound { if lastSym != sym.SymClosedRound {
err = errors.New("unterminated arguments list") err = errors.New("unterminated arguments list")
} else { } else {
tree = newFuncCallTerm(tk, args) tree = newFuncCallTerm(tk, args)
@@ -97,13 +98,13 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
// Example: "add = func(x,y) {x+y} // Example: "add = func(x,y) {x+y}
var body *scan.Ast var body *scan.Ast
args := make([]*scan.Term, 0) args := make([]*scan.Term, 0)
lastSym := scan.SymUnknown lastSym := sym.SymUnknown
defaultParamsStarted := false defaultParamsStarted := false
itemExpected := false itemExpected := false
tk := scanner.Previous() tk := scanner.Previous()
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos { for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.IsSymbol(scan.SymIdentifier) { if tk.IsSymbol(sym.SymIdentifier) {
param := scan.NewTerm(tk) param := scan.NewTerm(tk)
if len(args) > 0 { if len(args) > 0 {
if pos := paramAlreadyDefined(args, param); pos > 0 { if pos := paramAlreadyDefined(args, param); pos > 0 {
@@ -113,10 +114,10 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
} }
args = append(args, param) args = append(args, param)
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.Sym == scan.SymEqual { if tk.Sym == sym.SymEqual {
var paramExpr *scan.Ast var paramExpr *scan.Ast
defaultParamsStarted = true defaultParamsStarted = true
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, scan.SymComma, scan.SymClosedRound); err != nil { if paramExpr, err = parser.parseItem(scanner, parserNoFlags, sym.SymComma, sym.SymClosedRound); err != nil {
break break
} }
param.ForceChild(paramExpr.Root()) param.ForceChild(paramExpr.Root())
@@ -130,25 +131,25 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == scan.SymComma itemExpected = lastSym == sym.SymComma
} }
if err == nil && lastSym != scan.SymClosedRound { if err == nil && lastSym != sym.SymClosedRound {
err = tk.ErrorExpectedGot(")") err = tk.ErrorExpectedGot(")")
} }
if err == nil { if err == nil {
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.IsSymbol(scan.SymOpenBrace) { if tk.IsSymbol(sym.SymOpenBrace) {
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymClosedBrace) body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, sym.SymClosedBrace)
} else { } else {
err = tk.ErrorExpectedGot("{") err = tk.ErrorExpectedGot("{")
} }
} }
if err == nil { if err == nil {
if scanner.Previous().Sym != scan.SymClosedBrace { if scanner.Previous().Sym != sym.SymClosedBrace {
err = scanner.Previous().ErrorExpectedGot("}") err = scanner.Previous().ErrorExpectedGot("}")
} else { } else {
tk = scanner.MakeValueToken(scan.SymExpression, "", body) tk = scanner.MakeValueToken(sym.SymExpression, "", body)
tree = newFuncDefTerm(tk, args) tree = newFuncDefTerm(tk, args)
} }
} }
@@ -165,40 +166,35 @@ func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
return return
} }
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym scan.Symbol) (listTerm *scan.Term, err error) { func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym sym.Symbol) (listTerm *scan.Term, err error) {
r, c := scanner.LastPos() r, c := scanner.LastPos()
args := make([]*scan.Term, 0) args := make([]*scan.Term, 0)
lastSym := scan.SymUnknown lastSym := sym.SymUnknown
itemExpected := false itemExpected := false
itemCtx := remFlags(ctx, allowIndex) itemCtx := remFlags(ctx, allowIndex)
for lastSym != termSym && lastSym != scan.SymEos { for lastSym != termSym && lastSym != sym.SymEos {
zeroRequired := scanner.Current().Sym == scan.SymColon
var itemTree *scan.Ast var itemTree *scan.Ast
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, termSym); err == nil { if itemTree, err = parser.parseItem(scanner, itemCtx, sym.SymComma, termSym); err == nil {
root := itemTree.Root() root := itemTree.Root()
if root != nil { if root != nil {
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon { if root.Symbol() == sym.SymDoubleDot {
changeColonToRange(root) if hasFlag(ctx, allowIndex) {
} changeColonToInterval(root)
if !hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange { } else {
// err = root.Errorf("unexpected range expression") // err = root.Errorf("unexpected range expression")
err = errRangeUnexpectedExpression(root) err = errIntervalUnexpectedExpression(root)
break
}
args = append(args, root)
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.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 = errRangeInvalidSpecification(root)
break break
} }
zeroTk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymInteger, "0", int64(0))
zeroTerm := scan.NewTerm(zeroTk)
zeroTerm.SetParent(root)
root.Children[0] = zeroTerm
} }
// if hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymDoubleDot {
// changeColonToInterval(root)
// }
// if !hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymInterval {
// // err = root.Errorf("unexpected range expression")
// err = errIntervalUnexpectedExpression(root)
// break
// }
args = append(args, root)
} else if itemExpected { } else if itemExpected {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("list-item") err = prev.ErrorExpectedGot("list-item")
@@ -208,16 +204,16 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSy
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
if itemExpected = lastSym == scan.SymComma; itemExpected { if itemExpected = lastSym == sym.SymComma; itemExpected {
remFlags(ctx, allowIndex) remFlags(ctx, allowIndex)
} }
} }
if err == nil { if err == nil {
if lastSym != termSym { if lastSym != termSym {
err = scanner.Previous().ErrorExpectedGot("]") err = scanner.Previous().ErrorExpectedGot("]")
} else if termSym == scan.SymClosedSquare { } else if termSym == sym.SymClosedSquare {
listTerm = newListTerm(r, c, args) listTerm = newListTerm(r, c, args)
} else if termSym == scan.SymGreaterClosedSquare { } else if termSym == sym.SymGreaterClosedSquare {
listTerm = newLinkedListTerm(r, c, args) listTerm = newLinkedListTerm(r, c, args)
} else { } else {
err = fmt.Errorf("[%d:%d] unknown list type", r, c) err = fmt.Errorf("[%d:%d] unknown list type", r, c)
@@ -229,11 +225,11 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSy
func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) { func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
tk := scanner.Previous() tk := scanner.Previous()
args := make([]*scan.Term, 0) args := make([]*scan.Term, 0)
lastSym := scan.SymUnknown lastSym := sym.SymUnknown
itemExpected := false itemExpected := false
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos { for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
var subTree *scan.Ast var subTree *scan.Ast
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err == nil { if subTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.SymClosedRound); err == nil {
if subTree.Root() != nil { if subTree.Root() != nil {
args = append(args, subTree.Root()) args = append(args, subTree.Root())
} else if itemExpected { } else if itemExpected {
@@ -245,10 +241,10 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == scan.SymComma itemExpected = lastSym == sym.SymComma
} }
if err == nil { if err == nil {
if lastSym != scan.SymClosedRound { if lastSym != sym.SymClosedRound {
err = scanner.Previous().ErrorExpectedGot(")") err = scanner.Previous().ErrorExpectedGot(")")
} else { } else {
subtree = newIteratorTerm(tk, args) subtree = newIteratorTerm(tk, args)
@@ -259,16 +255,16 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
// func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) { // func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
// tk := parser.Next(scanner) // tk := parser.Next(scanner)
// if tk.Sym == scan.SymError { // if tk.Sym == sym.SymError {
// err = tk.Error() // err = tk.Error()
// return // return
// } // }
// if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos { // if tk.Sym == sym.SymClosedBrace || tk.Sym == sym.SymEos {
// return // return
// } // }
// if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString || tk.Sym == scan.SymIdentifier { // if tk.Sym == sym.SymInteger || tk.Sym == sym.SymString || tk.Sym == sym.SymIdentifier {
// tkSep := parser.Next(scanner) // tkSep := parser.Next(scanner)
// if tkSep.Sym != scan.SymColon { // if tkSep.Sym != sym.SymColon {
// err = tkSep.ErrorExpectedGot(":") // err = tkSep.ErrorExpectedGot(":")
// } else { // } else {
// key = tk.Value // key = tk.Value
@@ -281,15 +277,15 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) { func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) {
var keyTree *scan.Ast var keyTree *scan.Ast
if keyTree, err = parser.parseItem(scanner, parserNoFlags, scan.SymColon, scan.SymClosedBrace); err == nil { if keyTree, err = parser.parseItem(scanner, parserNoFlags, sym.SymColon, sym.SymClosedBrace); err == nil {
key = keyTree.Root() key = keyTree.Root()
tkSep := scanner.Previous() tkSep := scanner.Previous()
sym := tkSep.Sym symbol := tkSep.Sym
if sym == scan.SymClosedBrace || sym == scan.SymEos { if symbol == sym.SymClosedBrace || symbol == sym.SymEos {
if key != nil { if key != nil {
err = tkSep.ErrorExpectedGot(":") err = tkSep.ErrorExpectedGot(":")
} }
} else if sym != scan.SymColon { } else if symbol != sym.SymColon {
err = tkSep.ErrorExpectedGot(":") err = tkSep.ErrorExpectedGot(":")
} }
} }
@@ -298,9 +294,9 @@ func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err e
func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) { func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
args := make(map[any]*scan.Term, 0) args := make(map[any]*scan.Term, 0)
lastSym := scan.SymUnknown lastSym := sym.SymUnknown
itemExpected := false itemExpected := false
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos { for lastSym != sym.SymClosedBrace && lastSym != sym.SymEos {
var valueTree *scan.Ast var valueTree *scan.Ast
var key any var key any
if key, err = parser.parseDictKey(scanner); err != nil { if key, err = parser.parseDictKey(scanner); err != nil {
@@ -313,7 +309,7 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
} }
break break
} }
if valueTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil { if valueTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.SymClosedBrace); err == nil {
if valueTree.Root() != nil { if valueTree.Root() != nil {
args[key] = valueTree.Root() args[key] = valueTree.Root()
} else /*if key != nil*/ { } else /*if key != nil*/ {
@@ -325,10 +321,10 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == scan.SymComma itemExpected = lastSym == sym.SymComma
} }
if err == nil { if err == nil {
if lastSym != scan.SymClosedBrace { if lastSym != sym.SymClosedBrace {
err = scanner.Previous().ErrorExpectedGot("}") err = scanner.Previous().ErrorExpectedGot("}")
} else { } else {
subtree = newDictTerm(args) subtree = newDictTerm(args)
@@ -344,12 +340,12 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
tk := parser.Next(scanner) tk := parser.Next(scanner)
startRow := tk.Row() startRow := tk.Row()
startCol := tk.Col() startCol := tk.Col()
if tk.Sym == scan.SymOpenSquare { if tk.Sym == sym.SymOpenSquare {
if defaultCase { if defaultCase {
err = tk.Errorf("case list in default clause") err = tk.Errorf("case list in default clause")
return return
} }
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), scan.SymClosedSquare); err != nil { if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), sym.SymClosedSquare); err != nil {
return return
} }
tk = parser.Next(scanner) tk = parser.Next(scanner)
@@ -359,8 +355,8 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0)) filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0))
} }
if tk.Sym == scan.SymOpenBrace { if tk.Sym == sym.SymOpenBrace {
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, scan.SymClosedBrace); err != nil { if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, sym.SymClosedBrace); err != nil {
return return
} }
} else { } else {
@@ -390,7 +386,7 @@ func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx p
var caseTerm *scan.Term var caseTerm *scan.Term
ctx = remFlags(ctx, allowIndex) ctx = remFlags(ctx, allowIndex)
tk := scanner.MakeToken(scan.SymSelector, '?') tk := scanner.MakeToken(sym.SymSelector, '?')
if selectorTerm, err = tree.AddToken(tk); err != nil { if selectorTerm, err = tree.AddToken(tk); err != nil {
return return
} }
@@ -401,11 +397,11 @@ func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx p
return return
} }
func (parser *parser) parseItem(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) { func (parser *parser) parseItem(scanner *scan.Scanner, ctx parserContext, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...) return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
} }
func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) { func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if errVal, ok := r.(error); ok { if errVal, ok := r.(error); ok {
@@ -415,23 +411,23 @@ func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (
} }
} }
}() }()
termSymbols = append(termSymbols, scan.SymEos) termSymbols = append(termSymbols, sym.SymEos)
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...) return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
} }
func couldBeACollection(t *scan.Term) bool { func couldBeACollection(t *scan.Term) bool {
var sym = scan.SymUnknown var symbol = sym.SymUnknown
if t != nil { if t != nil {
sym = t.Symbol() symbol = t.Symbol()
} }
// return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable // return sym == sym.SymList || sym == sym.SymString || sym == sym.SymDict || sym == sym.SymExpression || sym == sym.SymVariable
return slices.Contains([]scan.Symbol{scan.SymList, scan.SymLinkedList, scan.SymString, scan.SymDict, scan.SymExpression, scan.SymVariable, scan.SymIndex}, sym) return slices.Contains([]sym.Symbol{sym.SymList, sym.SymLinkedList, sym.SymString, sym.SymDict, sym.SymExpression, sym.SymVariable, sym.SymIndex}, symbol)
} }
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) { func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
var tk *scan.Token var tk *scan.Token
if allowIndeces { if allowIndeces {
tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), scan.SymIndex, listTerm.Source()) tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), sym.SymIndex, listTerm.Source())
root = scan.NewTerm(tk) root = scan.NewTerm(tk)
if err = tree.AddTerm(root); err == nil { if err = tree.AddTerm(root); err == nil {
err = tree.AddTerm(listTerm) err = tree.AddTerm(listTerm)
@@ -445,22 +441,22 @@ func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *
func changePrefix(tk *scan.Token) { func changePrefix(tk *scan.Token) {
switch tk.Sym { switch tk.Sym {
case scan.SymMinus: case sym.SymMinus:
tk.SetSymbol(scan.SymChangeSign) tk.SetSymbol(sym.SymChangeSign)
case scan.SymPlus: case sym.SymPlus:
tk.SetSymbol(scan.SymUnchangeSign) tk.SetSymbol(sym.SymUnchangeSign)
case scan.SymStar: case sym.SymStar:
tk.SetSymbol(scan.SymDereference) tk.SetSymbol(sym.SymDereference)
case scan.SymExclamation: case sym.SymExclamation:
tk.SetSymbol(scan.SymNot) tk.SetSymbol(sym.SymNot)
case scan.SymDoublePlus: case sym.SymDoublePlus:
tk.SetSymbol(scan.SymPreInc) tk.SetSymbol(sym.SymPreInc)
case scan.SymDoubleMinus: case sym.SymDoubleMinus:
tk.SetSymbol(scan.SymPreDec) tk.SetSymbol(sym.SymPreDec)
} }
} }
func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) { func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
var selectorTerm *scan.Term = nil var selectorTerm *scan.Term = nil
var currentTerm *scan.Term = nil var currentTerm *scan.Term = nil
var tk *scan.Token var tk *scan.Token
@@ -473,7 +469,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
// continue // continue
// } // }
if tk.Sym == scan.SymSemiColon { if tk.Sym == sym.SymSemiColon {
if hasFlag(ctx, allowMultiExpr) { if hasFlag(ctx, allowMultiExpr) {
tree.ToForest() tree.ToForest()
firstToken = true firstToken = true
@@ -493,9 +489,9 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
} }
switch tk.Sym { switch tk.Sym {
case scan.SymOpenRound: case sym.SymOpenRound:
var subTree *scan.Ast var subTree *scan.Ast
if subTree, err = parser.parseGeneral(scanner, ctx, scan.SymClosedRound); err == nil { if subTree, err = parser.parseGeneral(scanner, ctx, sym.SymClosedRound); err == nil {
if subTree.Root() == nil { if subTree.Root() == nil {
err = tk.ErrorExpectedGotString("expression", "()") err = tk.ErrorExpectedGotString("expression", "()")
} else { } else {
@@ -504,26 +500,26 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
currentTerm = exprTerm currentTerm = exprTerm
} }
} }
case scan.SymFuncCall: case sym.SymFuncCall:
var funcCallTerm *scan.Term var funcCallTerm *scan.Term
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil { if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
err = tree.AddTerm(funcCallTerm) err = tree.AddTerm(funcCallTerm)
currentTerm = funcCallTerm currentTerm = funcCallTerm
} }
case scan.SymOpenSquare: case sym.SymOpenSquare:
var listTerm *scan.Term var listTerm *scan.Term
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm)) newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymClosedSquare); err == nil { if listTerm, err = parser.parseList(scanner, newCtx, sym.SymClosedSquare); err == nil {
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex)) currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
} }
case scan.SymOpenSquareLess: case sym.SymOpenSquareLess:
var listTerm *scan.Term var listTerm *scan.Term
newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false) newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false)
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymGreaterClosedSquare); err == nil { if listTerm, err = parser.parseList(scanner, newCtx, sym.SymGreaterClosedSquare); err == nil {
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex)) currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
} }
case scan.SymOpenBrace: case sym.SymOpenBrace:
if currentTerm != nil && currentTerm.Symbol() == scan.SymColon { if currentTerm != nil && currentTerm.Symbol() == sym.SymColon {
err = currentTerm.Errorf(`selector-case outside of a selector context`) err = currentTerm.Errorf(`selector-case outside of a selector context`)
} else { } else {
var mapTerm *scan.Term var mapTerm *scan.Term
@@ -532,54 +528,61 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
currentTerm = mapTerm currentTerm = mapTerm
} }
} }
case scan.SymEqual, scan.SymPlusEqual, scan.SymMinusEqual, scan.SymStarEqual, scan.SymSlashEqual, scan.SymPercEqual, scan.SymAmpersandEqual, scan.SymVertBarEqual, scan.SymDoubleLessEqual, scan.SymDoubleGreaterEqual, scan.SymCaretEqual: case sym.SymEqual, sym.SymPlusEqual, sym.SymMinusEqual, sym.SymStarEqual, sym.SymSlashEqual, sym.SymPercEqual, sym.SymAmpersandEqual, sym.SymVertBarEqual, sym.SymDoubleLessEqual, sym.SymDoubleGreaterEqual, sym.SymCaretEqual:
currentTerm, err = tree.AddToken(tk) currentTerm, err = tree.AddToken(tk)
firstToken = true firstToken = true
case scan.SymFuncDef: case sym.SymFuncDef:
var funcDefTerm *scan.Term var funcDefTerm *scan.Term
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil { if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
err = tree.AddTerm(funcDefTerm) err = tree.AddTerm(funcDefTerm)
currentTerm = funcDefTerm currentTerm = funcDefTerm
} }
case scan.SymDollarRound: case sym.SymDollarRound:
var iterDefTerm *scan.Term var iterDefTerm *scan.Term
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil { if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
err = tree.AddTerm(iterDefTerm) err = tree.AddTerm(iterDefTerm)
currentTerm = iterDefTerm currentTerm = iterDefTerm
} }
case scan.SymIdentifier: case sym.SymIdentifier:
if tk.Source()[0] == '@' && !hasFlag(ctx, allowVarRef) { if tk.Source()[0] == '@' && !hasFlag(ctx, allowVarRef) {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.Source()) err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.Source())
} else { } else {
currentTerm, err = tree.AddToken(tk) currentTerm, err = tree.AddToken(tk)
} }
case scan.SymQuestion: case sym.SymQuestion:
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil { if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
currentTerm = selectorTerm currentTerm = selectorTerm
addFlags(ctx, selectorContext) addFlags(ctx, selectorContext)
} }
case scan.SymColon, scan.SymDoubleColon: case sym.SymColon, sym.SymDoubleColon:
var caseTerm *scan.Term var caseTerm *scan.Term
if selectorTerm != nil { if selectorTerm != nil {
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == scan.SymDoubleColon); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == sym.SymDoubleColon); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
currentTerm = caseTerm currentTerm = caseTerm
if tk.Sym == scan.SymDoubleColon { if tk.Sym == sym.SymDoubleColon {
selectorTerm = nil selectorTerm = nil
} }
} }
} else { } else {
// err = tk.Errorf("colon outside a selector context")
currentTerm, err = tree.AddToken(tk) currentTerm, err = tree.AddToken(tk)
if tk.IsOneOfA(scan.SymColon, scan.SymRange) { // Colon outside a selector term acts like a separator
// Colon outside a selector term acts like a separator firstToken = tk.IsOneOfA(sym.SymColon, sym.SymInterval)
firstToken = true
}
} }
case sym.SymDoubleDot:
currentTerm, err = tree.AddToken(tk)
if currentTerm.GetLeftChild() == nil {
zeroTk := scan.NewValueToken(tk.Row(), tk.Col(), sym.SymInteger, "0", int64(0))
tree.AddToken(zeroTk)
}
// Double dot acts like a separator
firstToken = tk.IsOneOfA(sym.SymDoubleDot, sym.SymInterval)
default: default:
currentTerm, err = tree.AddToken(tk) currentTerm, err = tree.AddToken(tk)
} }
if currentTerm != nil && currentTerm.Tk.Sym != scan.SymSelector && currentTerm.Parent != nil && currentTerm.Parent.Tk.Sym != scan.SymSelector { if currentTerm != nil && currentTerm.Tk.Sym != sym.SymSelector && currentTerm.Parent != nil && currentTerm.Parent.Tk.Sym != sym.SymSelector {
selectorTerm = nil selectorTerm = nil
remFlags(ctx, selectorContext) remFlags(ctx, selectorContext)
} }
@@ -589,7 +592,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
if err == nil { if err == nil {
if !tk.IsOneOf(termSymbols) { if !tk.IsOneOf(termSymbols) {
var symDesc string var symDesc string
if tk.IsSymbol(scan.SymError) { if tk.IsSymbol(sym.SymError) {
symDesc = tk.ErrorText() symDesc = tk.ErrorText()
} else { } else {
symDesc = scan.SymToString(tk.Sym) symDesc = scan.SymToString(tk.Sym)
+161 -133
View File
@@ -12,21 +12,23 @@ import (
"strconv" "strconv"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
) )
type Scanner struct { type Scanner struct {
current *Token current *Token
prev *Token prev *Token
stage *Token stage *Token
stream *bufio.Reader stream *bufio.Reader
row int row int
column int column int
translations map[Symbol]Symbol translations map[sym.Symbol]sym.Symbol
unreadByte byte
prevRowLength int
} }
func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *Scanner) { func NewScanner(s io.Reader, translations map[sym.Symbol]sym.Symbol) (inst *Scanner) {
inst = &Scanner{ inst = &Scanner{
stream: bufio.NewReader(s), stream: bufio.NewReader(s),
row: 1, row: 1,
@@ -37,15 +39,15 @@ func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *Scanner) {
return inst return inst
} }
func DefaultTranslations() map[Symbol]Symbol { func DefaultTranslations() map[sym.Symbol]sym.Symbol {
return map[Symbol]Symbol{ return map[sym.Symbol]sym.Symbol{
SymDoubleAmpersand: SymAnd, sym.SymDoubleAmpersand: sym.SymAnd,
SymKwAnd: SymAnd, sym.SymKwAnd: sym.SymAnd,
SymDoubleVertBar: SymOr, sym.SymDoubleVertBar: sym.SymOr,
SymKwOr: SymOr, sym.SymKwOr: sym.SymOr,
// SymTilde: SymNot, sym.SymKwNot: sym.SymNot,
SymKwNot: SymNot, sym.SymLessGreater: sym.SymNotEqual,
SymLessGreater: SymNotEqual, // SymTilde: sym.SymNot,
} }
} }
@@ -57,9 +59,25 @@ func (scanner *Scanner) Current() *Token {
return scanner.current return scanner.current
} }
func (scanner *Scanner) setUnreadChar(ch byte) {
scanner.column--
if scanner.column == 0 {
scanner.column = scanner.prevRowLength
scanner.row--
}
scanner.unreadByte = ch
}
func (scanner *Scanner) readChar() (ch byte, err error) { func (scanner *Scanner) readChar() (ch byte, err error) {
if ch, err = scanner.stream.ReadByte(); err == nil { if scanner.unreadByte != 0 {
ch = scanner.unreadByte
scanner.unreadByte = 0
} else {
ch, err = scanner.stream.ReadByte()
}
if err == nil {
if ch == '\n' { if ch == '\n' {
scanner.prevRowLength = scanner.column
scanner.row++ scanner.row++
scanner.column = 0 scanner.column = 0
} else { } else {
@@ -128,237 +146,237 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
switch ch { switch ch {
case '+': case '+':
if next, _ := scanner.peek(); next == '+' { if next, _ := scanner.peek(); next == '+' {
tk = scanner.moveOn(SymDoublePlus, ch, next) tk = scanner.moveOn(sym.SymDoublePlus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymPlusEqual, ch, next) tk = scanner.moveOn(sym.SymPlusEqual, ch, next)
} else if next == '>' { } else if next == '>' {
tk = scanner.moveOn(SymPlusGreater, ch, next) tk = scanner.moveOn(sym.SymPlusGreater, ch, next)
} else { } else {
tk = scanner.MakeToken(SymPlus, ch) tk = scanner.MakeToken(sym.SymPlus, ch)
} }
case '-': case '-':
if next, _ := scanner.peek(); next == '-' { if next, _ := scanner.peek(); next == '-' {
tk = scanner.moveOn(SymDoubleMinus, ch, next) tk = scanner.moveOn(sym.SymDoubleMinus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymMinusEqual, ch, next) tk = scanner.moveOn(sym.SymMinusEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymMinus, ch) tk = scanner.MakeToken(sym.SymMinus, ch)
} }
case '*': case '*':
if next, _ := scanner.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
tk = scanner.moveOn(SymDoubleStar, ch, next) tk = scanner.moveOn(sym.SymDoubleStar, ch, next)
// } else if next == '/' { // } else if next == '/' {
// tk = self.moveOn(SymClosedComment, ch, next) // tk = self.moveOn(SymClosedComment, ch, next)
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymStarEqual, ch, next) tk = scanner.moveOn(sym.SymStarEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymStar, ch) tk = scanner.MakeToken(sym.SymStar, ch)
} }
case '/': case '/':
if next, _ := scanner.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
scanner.readChar() scanner.readChar()
tk = scanner.fetchBlockComment() tk = scanner.fetchBlockComment()
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymSlashEqual, ch, next) tk = scanner.moveOn(sym.SymSlashEqual, ch, next)
} else if next == '/' { } else if next == '/' {
scanner.readChar() scanner.readChar()
tk = scanner.fetchOnLineComment() tk = scanner.fetchOnLineComment()
} else { } else {
tk = scanner.MakeToken(SymSlash, ch) tk = scanner.MakeToken(sym.SymSlash, ch)
} }
case '\\': case '\\':
if escape { if escape {
tk = scanner.MakeToken(SymBackSlash, ch) tk = scanner.MakeToken(sym.SymBackSlash, ch)
escape = false escape = false
} else { } else {
escape = true escape = true
} }
case '|': case '|':
if next, _ := scanner.peek(); next == '|' { if next, _ := scanner.peek(); next == '|' {
tk = scanner.moveOn(SymDoubleVertBar, ch, next) tk = scanner.moveOn(sym.SymDoubleVertBar, ch, next)
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymVertBarEqual, ch, next) tk = scanner.moveOn(sym.SymVertBarEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymVertBar, ch) tk = scanner.MakeToken(sym.SymVertBar, ch)
} }
case ',': case ',':
tk = scanner.MakeToken(SymComma, ch) tk = scanner.MakeToken(sym.SymComma, ch)
case '^': case '^':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymCaretEqual, ch, next) tk = scanner.moveOn(sym.SymCaretEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymCaret, ch) tk = scanner.MakeToken(sym.SymCaret, ch)
} }
case ':': case ':':
if next, _ := scanner.peek(); next == ':' { if next, _ := scanner.peek(); next == ':' {
tk = scanner.moveOn(SymDoubleColon, ch, next) tk = scanner.moveOn(sym.SymDoubleColon, ch, next)
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymColonEqual, ch, next) tk = scanner.moveOn(sym.SymColonEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymColon, ch) tk = scanner.MakeToken(sym.SymColon, ch)
} }
case ';': case ';':
tk = scanner.MakeToken(SymSemiColon, ch) tk = scanner.MakeToken(sym.SymSemiColon, ch)
case '.': case '.':
//if next, _ := self.peek(); next >= '0' && next <= '9' { //if next, _ := self.peek(); next >= '0' && next <= '9' {
// tk = self.parseNumber(ch) // tk = self.parseNumber(ch)
//} else if next == '/' { //} else if next == '/' {
if next, _ := scanner.peek(); next == '/' { if next, _ := scanner.peek(); next == '/' {
tk = scanner.moveOn(SymDotSlash, ch, next) tk = scanner.moveOn(sym.SymDotSlash, ch, next)
} else if next == '.' { } else if next == '.' {
tk = scanner.moveOn(sym.SymDoubleDot, ch, next)
// TODO: this section needs verification
if next1, _ := scanner.peek(); next1 == '.' { if next1, _ := scanner.peek(); next1 == '.' {
tk = scanner.moveOn(SymTripleDot, ch, next, next1) tk = scanner.moveOn(sym.SymTripleDot, next, next1)
} else {
tk = scanner.moveOn(SymDoubleDot, ch, next)
} }
} else { } else {
tk = scanner.MakeToken(SymDot, ch) tk = scanner.MakeToken(sym.SymDot, ch)
} }
case '\'': case '\'':
if escape { if escape {
tk = scanner.MakeToken(SymQuote, ch) tk = scanner.MakeToken(sym.SymQuote, ch)
escape = false escape = false
} else { } else {
tk = scanner.fetchString(ch, true) tk = scanner.fetchString(ch, true)
} }
case '"': case '"':
if escape { if escape {
tk = scanner.MakeToken(SymDoubleQuote, ch) tk = scanner.MakeToken(sym.SymDoubleQuote, ch)
escape = false escape = false
} else { } else {
tk = scanner.fetchString(ch, true) tk = scanner.fetchString(ch, true)
} }
case '`': case '`':
tk = scanner.MakeToken(SymBackTick, ch) tk = scanner.MakeToken(sym.SymBackTick, ch)
case '!': case '!':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymNotEqual, ch, next) tk = scanner.moveOn(sym.SymNotEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymExclamation, ch) tk = scanner.MakeToken(sym.SymExclamation, ch)
} }
case '?': case '?':
if next, _ := scanner.peek(); next == '?' { if next, _ := scanner.peek(); next == '?' {
tk = scanner.moveOn(SymDoubleQuestion, ch, next) tk = scanner.moveOn(sym.SymDoubleQuestion, ch, next)
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymQuestionEqual, ch, next) tk = scanner.moveOn(sym.SymQuestionEqual, ch, next)
} else if next == '!' { } else if next == '!' {
tk = scanner.moveOn(SymQuestionExclam, ch, next) tk = scanner.moveOn(sym.SymQuestionExclam, ch, next)
} else { } else {
tk = scanner.MakeToken(SymQuestion, ch) tk = scanner.MakeToken(sym.SymQuestion, ch)
} }
case '&': case '&':
if next, _ := scanner.peek(); next == '&' { if next, _ := scanner.peek(); next == '&' {
tk = scanner.moveOn(SymDoubleAmpersand, ch, next) tk = scanner.moveOn(sym.SymDoubleAmpersand, ch, next)
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymAmpersandEqual, ch, next) tk = scanner.moveOn(sym.SymAmpersandEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymAmpersand, ch) tk = scanner.MakeToken(sym.SymAmpersand, ch)
} }
case '%': case '%':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymPercEqual, ch, next) tk = scanner.moveOn(sym.SymPercEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymPercent, ch) tk = scanner.MakeToken(sym.SymPercent, ch)
} }
case '#': case '#':
tk = scanner.MakeToken(SymHash, ch) tk = scanner.MakeToken(sym.SymHash, ch)
case '@': case '@':
if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') { if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
scanner.readChar() scanner.readChar()
if tk = scanner.fetchIdentifier(next); tk.Sym == SymIdentifier { if tk = scanner.fetchIdentifier(next); tk.Sym == sym.SymIdentifier {
//tk.Sym = SymIdRef //tk.Sym = SymIdRef
tk.source = "@" + tk.source tk.source = "@" + tk.source
} else { } else {
tk = scanner.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source)) tk = scanner.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source))
} }
} else if next == '@' { } else if next == '@' {
tk = scanner.moveOn(SymDoubleAt, ch, next) tk = scanner.moveOn(sym.SymDoubleAt, ch, next)
} else { } else {
tk = scanner.MakeToken(SymAt, ch) tk = scanner.MakeToken(sym.SymAt, ch)
} }
case '_': case '_':
tk = scanner.MakeToken(SymUndescore, ch) tk = scanner.MakeToken(sym.SymUndescore, ch)
case '=': case '=':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymDoubleEqual, ch, next) tk = scanner.moveOn(sym.SymDoubleEqual, ch, next)
} else { } else {
tk = scanner.MakeToken(SymEqual, ch) tk = scanner.MakeToken(sym.SymEqual, ch)
} }
case '<': case '<':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymLessOrEqual, ch, next) tk = scanner.moveOn(sym.SymLessOrEqual, ch, next)
} else if next == '<' { } else if next == '<' {
scanner.readChar() scanner.readChar()
next2, _ := scanner.readChar() next2, _ := scanner.readChar()
scanner.unreadChar() scanner.unreadChar()
if next2 == '=' { if next2 == '=' {
tk = scanner.moveOn(SymDoubleLessEqual, ch, next, next2) tk = scanner.moveOn(sym.SymDoubleLessEqual, ch, next, next2)
} else { } else {
tk = scanner.accept(SymDoubleLess, ch, next) tk = scanner.accept(sym.SymDoubleLess, ch, next)
} }
} else if next == '>' { } else if next == '>' {
tk = scanner.moveOn(SymLessGreater, ch, next) tk = scanner.moveOn(sym.SymLessGreater, ch, next)
} else if next == '+' { } else if next == '+' {
tk = scanner.moveOn(SymLessPlus, ch, next) tk = scanner.moveOn(sym.SymLessPlus, ch, next)
} else { } else {
tk = scanner.MakeToken(SymLess, ch) tk = scanner.MakeToken(sym.SymLess, ch)
} }
case '>': case '>':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymGreaterOrEqual, ch, next) tk = scanner.moveOn(sym.SymGreaterOrEqual, ch, next)
} else if next == '>' { } else if next == '>' {
scanner.readChar() scanner.readChar()
next2, _ := scanner.readChar() next2, _ := scanner.readChar()
scanner.unreadChar() scanner.unreadChar()
if next2 == '=' { if next2 == '=' {
tk = scanner.moveOn(SymDoubleGreaterEqual, ch, next, next2) tk = scanner.moveOn(sym.SymDoubleGreaterEqual, ch, next, next2)
} else { } else {
tk = scanner.accept(SymDoubleGreater, ch, next) tk = scanner.accept(sym.SymDoubleGreater, ch, next)
} }
} else if next == ']' { } else if next == ']' {
tk = scanner.moveOn(SymGreaterClosedSquare, ch, next) tk = scanner.moveOn(sym.SymGreaterClosedSquare, ch, next)
} else { } else {
tk = scanner.MakeToken(SymGreater, ch) tk = scanner.MakeToken(sym.SymGreater, ch)
} }
case '$': case '$':
if next, _ := scanner.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = scanner.moveOn(SymDollarRound, ch, next) tk = scanner.moveOn(sym.SymDollarRound, ch, next)
tk.source += ")" tk.source += ")"
} else if next == '$' { } else if next == '$' {
tk = scanner.moveOn(SymDoubleDollar, ch, next) tk = scanner.moveOn(sym.SymDoubleDollar, ch, next)
} else if next == '{' { } else if next == '{' {
scanner.readChar() scanner.readChar()
if tk = scanner.fetchString('}', false); tk != nil { if tk = scanner.fetchString('}', false); tk != nil {
tk.Sym = SymIdentifier tk.Sym = sym.SymIdentifier
} }
} else if next == '_' || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') { } else if next == '_' || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
scanner.readChar() scanner.readChar()
tk = scanner.fetchIdentifier(next) tk = scanner.fetchIdentifier(next)
} else { } else {
tk = scanner.MakeToken(SymDollar, ch) tk = scanner.MakeToken(sym.SymDollar, ch)
} }
case '(': case '(':
// if next, _ := scanner.peek(); next == ')' { // if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next) // tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else { // } else {
tk = scanner.MakeToken(SymOpenRound, ch) tk = scanner.MakeToken(sym.SymOpenRound, ch)
// } // }
case ')': case ')':
tk = scanner.MakeToken(SymClosedRound, ch) tk = scanner.MakeToken(sym.SymClosedRound, ch)
case '[': case '[':
if next, _ := scanner.peek(); next == '<' { if next, _ := scanner.peek(); next == '<' {
tk = scanner.moveOn(SymOpenSquareLess, ch, next) tk = scanner.moveOn(sym.SymOpenSquareLess, ch, next)
} else { } else {
tk = scanner.MakeToken(SymOpenSquare, ch) tk = scanner.MakeToken(sym.SymOpenSquare, ch)
} }
case ']': case ']':
tk = scanner.MakeToken(SymClosedSquare, ch) tk = scanner.MakeToken(sym.SymClosedSquare, ch)
case '{': case '{':
tk = scanner.MakeToken(SymOpenBrace, ch) tk = scanner.MakeToken(sym.SymOpenBrace, ch)
case '}': case '}':
tk = scanner.MakeToken(SymClosedBrace, ch) tk = scanner.MakeToken(sym.SymClosedBrace, ch)
case '~': case '~':
tk = scanner.MakeToken(SymTilde, ch) tk = scanner.MakeToken(sym.SymTilde, ch)
case 0: case 0:
if escape { if escape {
tk = scanner.makeErrorToken(errors.New("incomplete escape sequence")) tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
@@ -366,9 +384,9 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
escape = false escape = false
default: default:
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
if tk = scanner.fetchIdentifier(ch); tk.Sym == SymKwFunc { if tk = scanner.fetchIdentifier(ch); tk.Sym == sym.SymKwFunc {
if next, _ := scanner.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = scanner.moveOn(SymFuncDef, ch, next) tk = scanner.moveOn(sym.SymFuncDef, ch, next)
} }
} }
} else if ch >= '0' && ch <= '9' { } else if ch >= '0' && ch <= '9' {
@@ -416,19 +434,20 @@ func (scanner *Scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int
numBase = 10 numBase = 10
if ch, err = scanner.peek(); err == nil { if ch, err = scanner.peek(); err == nil {
if ch == 'b' || ch == 'B' { switch ch {
case 'b', 'B':
numBase = 2 numBase = 2
digitType = "binary" digitType = "binary"
scanner.readChar() scanner.readChar()
digitFunc = isBinaryDigit digitFunc = isBinaryDigit
firstCh, err = scanner.readChar() firstCh, err = scanner.readChar()
} else if ch == 'o' || ch == 'O' { case 'o', 'O':
numBase = 8 numBase = 8
digitType = "octal" digitType = "octal"
scanner.readChar() scanner.readChar()
digitFunc = isOctalDigit digitFunc = isOctalDigit
firstCh, err = scanner.readChar() firstCh, err = scanner.readChar()
} else if ch == 'x' || ch == 'X' { case 'x', 'X':
numBase = 16 numBase = 16
digitType = "hex" digitType = "hex"
scanner.readChar() scanner.readChar()
@@ -450,7 +469,7 @@ func (scanner *Scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int
func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) { func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sym Symbol = SymInteger var symbol sym.Symbol = sym.SymInteger
var sb strings.Builder var sb strings.Builder
var isDigit func(byte) bool = isDecimalDigit var isDigit func(byte) bool = isDecimalDigit
var numBase = 10 var numBase = 10
@@ -464,18 +483,24 @@ func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
if numBase == 10 { if numBase == 10 {
if err == nil && ch == '.' { if err == nil && ch == '.' {
sym = SymFloat if next, _ := scanner.peek(); next == '.' {
sb.WriteByte(ch) // do nothing, this is an interval operator
ch, err = scanner.readChar() scanner.setUnreadChar('.')
if ch >= '0' && ch <= '9' { } else {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() { symbol = sym.SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = scanner.readChar()
if ch >= '0' && ch <= '9' {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch)
}
} }
} }
} }
if err == nil { if err == nil {
if ch == 'e' || ch == 'E' { switch ch {
sym = SymFloat case 'e', 'E':
symbol = sym.SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
if ch, err = scanner.readChar(); err == nil { if ch, err = scanner.readChar(); err == nil {
if ch == '+' || ch == '-' { if ch == '+' || ch == '-' {
@@ -490,8 +515,8 @@ func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", scanner.row, scanner.column, ch) err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", scanner.row, scanner.column, ch)
} }
} }
} else if ch == '(' { case '(':
sym = SymFraction symbol = sym.SymFraction
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = scanner.readChar() ch, err = scanner.readChar()
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
@@ -515,18 +540,21 @@ func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
var value any var value any
_ = scanner.sync(err) // TODO: Check this function _ = scanner.sync(err) // TODO: Check this function
txt := sb.String() txt := sb.String()
if sym == SymFloat { switch symbol {
case sym.SymFloat:
value, err = strconv.ParseFloat(txt, 64) value, err = strconv.ParseFloat(txt, 64)
} else if sym == SymFraction { case sym.SymFraction:
value, err = kern.MakeGeneratingFraction(txt) // value, err = kern.MakeGeneratingFraction(txt)
} else { value = txt
default:
value, err = strconv.ParseInt(txt, numBase, 64) value, err = strconv.ParseInt(txt, numBase, 64)
} }
if err == nil { tk = scanner.MakeValueToken(symbol, txt, value)
tk = scanner.MakeValueToken(sym, txt, value) // if err == nil {
} else { // tk = scanner.MakeValueToken(sym, txt, value)
tk = scanner.makeErrorToken(err) // } else {
} // tk = scanner.makeErrorToken(err)
// }
} }
return return
} }
@@ -545,17 +573,17 @@ func (scanner *Scanner) fetchIdentifier(firstCh byte) (tk *Token) {
} else { } else {
txt := sb.String() txt := sb.String()
uptxt := strings.ToUpper(txt) uptxt := strings.ToUpper(txt)
if sym, ok := keywords[uptxt]; ok { if symbol, ok := sym.Keywords[uptxt]; ok {
tk = scanner.makeKeywordToken(sym, uptxt) tk = scanner.makeKeywordToken(symbol, uptxt)
} else if uptxt == `TRUE` { } else if uptxt == `TRUE` {
tk = scanner.MakeValueToken(SymBool, txt, true) tk = scanner.MakeValueToken(sym.SymBool, txt, true)
} else if uptxt == `FALSE` { } else if uptxt == `FALSE` {
tk = scanner.MakeValueToken(SymBool, txt, false) tk = scanner.MakeValueToken(sym.SymBool, txt, false)
} else if ch, _ := scanner.peek(); ch == '(' { } else if ch, _ := scanner.peek(); ch == '(' {
scanner.readChar() scanner.readChar()
tk = scanner.MakeValueToken(SymFuncCall, txt+"(", txt) tk = scanner.MakeValueToken(sym.SymFuncCall, txt+"(", txt)
} else { } else {
tk = scanner.MakeValueToken(SymIdentifier, txt, txt) tk = scanner.MakeValueToken(sym.SymIdentifier, txt, txt)
} }
} }
@@ -576,14 +604,14 @@ func (scanner *Scanner) fetchIdentifier(firstCh byte) (tk *Token) {
} }
func (scanner *Scanner) fetchBlockComment() *Token { func (scanner *Scanner) fetchBlockComment() *Token {
return scanner.fetchUntil(SymComment, false, '*', '/') return scanner.fetchUntil(sym.SymComment, false, '*', '/')
} }
func (scanner *Scanner) fetchOnLineComment() *Token { func (scanner *Scanner) fetchOnLineComment() *Token {
return scanner.fetchUntil(SymComment, true, '\n') return scanner.fetchUntil(sym.SymComment, true, '\n')
} }
func (scanner *Scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) { func (scanner *Scanner) fetchUntil(sym sym.Symbol, allowEos bool, endings ...byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sb strings.Builder var sb strings.Builder
@@ -652,9 +680,9 @@ func (scanner *Scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
} else { } else {
txt := sb.String() txt := sb.String()
if addQuote { if addQuote {
tk = scanner.MakeValueToken(SymString, `"`+txt+`"`, txt) tk = scanner.MakeValueToken(sym.SymString, `"`+txt+`"`, txt)
} else { } else {
tk = scanner.MakeValueToken(SymString, txt, txt) tk = scanner.MakeValueToken(sym.SymString, txt, txt)
} }
} }
return return
@@ -676,7 +704,7 @@ func (scanner *Scanner) skipBlanks() (err error) {
return return
} }
func (scanner *Scanner) translate(sym Symbol) Symbol { func (scanner *Scanner) translate(sym sym.Symbol) sym.Symbol {
if scanner.translations != nil { if scanner.translations != nil {
if translatedSym, ok := scanner.translations[sym]; ok { if translatedSym, ok := scanner.translations[sym]; ok {
return translatedSym return translatedSym
@@ -685,7 +713,7 @@ func (scanner *Scanner) translate(sym Symbol) Symbol {
return sym return sym
} }
func (scanner *Scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) moveOn(sym sym.Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
// for i := 1; i < len(chars); i++ { // for i := 1; i < len(chars); i++ {
if len(chars) > 1 { if len(chars) > 1 {
@@ -695,22 +723,22 @@ func (scanner *Scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
return return
} }
func (scanner *Scanner) accept(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) accept(sym sym.Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (scanner *Scanner) MakeToken(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) MakeToken(sym sym.Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (scanner *Scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) { func (scanner *Scanner) makeKeywordToken(sym sym.Symbol, upperCaseKeyword string) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
return return
} }
func (scanner *Scanner) MakeValueToken(sym Symbol, source string, value any) (tk *Token) { func (scanner *Scanner) MakeValueToken(sym sym.Symbol, source string, value any) (tk *Token) {
tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value) tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
return return
} }
+132 -129
View File
@@ -1,14 +1,16 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// Symbol.go // symbol-map.go
package scan package scan
import ( import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/sym"
) )
var symbolMap map[Symbol]symbolSpec var symbolMap map[sym.Symbol]symbolSpec
type SymbolClass int16 type SymbolClass int16
@@ -31,138 +33,139 @@ type symbolSpec struct {
func init() { func init() {
symbolMap = map[Symbol]symbolSpec{ symbolMap = map[sym.Symbol]symbolSpec{
SymUnknown: {"<unknown>", SymClassOther, PosLeaf}, // -1: Unknown symbol sym.SymUnknown: {"<unknown>", SymClassOther, PosLeaf}, // -1: Unknown symbol
SymNone: {"<null>", SymClassOther, PosLeaf}, // 0: Null value for variable of type symbol sym.SymNone: {"<null>", SymClassOther, PosLeaf}, // 0: Null value for variable of type symbol
SymError: {"<error>", SymClassOther, PosLeaf}, // 1: Error reading from stream sym.SymError: {"<error>", SymClassOther, PosLeaf}, // 1: Error reading from stream
SymEos: {"<eos>", SymClassOther, PosLeaf}, // 2: End of stream sym.SymEos: {"<eos>", SymClassOther, PosLeaf}, // 2: End of stream
SymMinus: {"-", SymClassOperator, PosInfix}, // 3: '-' sym.SymMinus: {"-", SymClassOperator, PosInfix}, // 3: '-'
SymMinusEqual: {"-=", SymClassOperator, PosInfix}, // 4: '-=' sym.SymMinusEqual: {"-=", SymClassOperator, PosInfix}, // 4: '-='
SymDoubleMinus: {"--", SymClassOperator, PosPostfix}, // 5: '--' sym.SymDoubleMinus: {"--", SymClassOperator, PosPostfix}, // 5: '--'
SymPlus: {"+", SymClassOperator, PosInfix}, // 6: '+' sym.SymPlus: {"+", SymClassOperator, PosInfix}, // 6: '+'
SymPlusEqual: {"+=", SymClassOperator, PosInfix}, // 7: '+=' sym.SymPlusEqual: {"+=", SymClassOperator, PosInfix}, // 7: '+='
SymDoublePlus: {"++", SymClassOperator, PosPostfix}, // 8: '++' sym.SymDoublePlus: {"++", SymClassOperator, PosPostfix}, // 8: '++'
SymStar: {"*", SymClassOperator, PosInfix}, // 9: '*' sym.SymStar: {"*", SymClassOperator, PosInfix}, // 9: '*'
SymDoubleStar: {"**", SymClassOperator, PosInfix}, // 10: '**' sym.SymDoubleStar: {"**", SymClassOperator, PosInfix}, // 10: '**'
SymSlash: {"/", SymClassOperator, PosInfix}, // 11: '/' sym.SymSlash: {"/", SymClassOperator, PosInfix}, // 11: '/'
SymBackSlash: {"\\", SymClassOperator, PosLeaf}, // 12: '\' sym.SymBackSlash: {"\\", SymClassOperator, PosLeaf}, // 12: '\'
SymVertBar: {"|", SymClassOperator, PosInfix}, // 13: '|' sym.SymVertBar: {"|", SymClassOperator, PosInfix}, // 13: '|'
SymDoubleVertBar: {"||", SymClassOperator, PosInfix}, // 14: '||' sym.SymDoubleVertBar: {"||", SymClassOperator, PosInfix}, // 14: '||'
SymComma: {",", SymClassOperator, PosInfix}, // 15: ',' sym.SymComma: {",", SymClassOperator, PosInfix}, // 15: ','
SymColon: {":", SymClassOperator, PosInfix}, // 16: ':' sym.SymColon: {":", SymClassOperator, PosInfix}, // 16: ':'
SymSemiColon: {";", SymClassOperator, PosInfix}, // 17: ';' sym.SymSemiColon: {";", SymClassOperator, PosInfix}, // 17: ';'
SymDot: {".", SymClassOperator, PosInfix}, // 18: '.' sym.SymDot: {".", SymClassOperator, PosInfix}, // 18: '.'
SymDotSlash: {"./", SymClassOperator, PosInfix}, // 19: './' sym.SymDotSlash: {"./", SymClassOperator, PosInfix}, // 19: './'
SymQuote: {"'", SymClassDelimiter, PosLeaf}, // 20: '\'' sym.SymQuote: {"'", SymClassDelimiter, PosLeaf}, // 20: '\''
SymDoubleQuote: {"\"", SymClassDelimiter, PosLeaf}, // 21: '"' sym.SymDoubleQuote: {"\"", SymClassDelimiter, PosLeaf}, // 21: '"'
SymBackTick: {"`", SymClassDelimiter, PosLeaf}, // 22: '`' sym.SymBackTick: {"`", SymClassDelimiter, PosLeaf}, // 22: '`'
SymExclamation: {"!", SymClassOperator, PosPostfix}, // 23: '!' sym.SymExclamation: {"!", SymClassOperator, PosPostfix}, // 23: '!'
SymQuestion: {"?", SymClassOperator, PosInfix}, // 24: '?' sym.SymQuestion: {"?", SymClassOperator, PosInfix}, // 24: '?'
SymAmpersand: {"&", SymClassOperator, PosInfix}, // 25: '&' sym.SymAmpersand: {"&", SymClassOperator, PosInfix}, // 25: '&'
SymDoubleAmpersand: {"&&", SymClassOperator, PosInfix}, // 26: '&&' sym.SymDoubleAmpersand: {"&&", SymClassOperator, PosInfix}, // 26: '&&'
SymPercent: {"%", SymClassOperator, PosInfix}, // 27: '%' sym.SymPercent: {"%", SymClassOperator, PosInfix}, // 27: '%'
SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@' sym.SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@'
SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_' sym.SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_'
SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '=' sym.SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '='
SymColonEqual: {":=", SymClassOperator, PosInfix}, // 31: ':=' sym.SymColonEqual: {":=", SymClassOperator, PosInfix}, // 31: ':='
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 32: '==' sym.SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 32: '=='
SymLess: {"<", SymClassOperator, PosInfix}, // 33: '<' sym.SymLess: {"<", SymClassOperator, PosInfix}, // 33: '<'
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 34: '<=' sym.SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 34: '<='
SymGreater: {">", SymClassOperator, PosInfix}, // 35: '>' sym.SymGreater: {">", SymClassOperator, PosInfix}, // 35: '>'
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 36: '>=' sym.SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 36: '>='
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 37: '<>' sym.SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 37: '<>'
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 38: '!=' sym.SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 38: '!='
SymDollar: {"$", SymClassOperator, PosPrefix}, // 39: '$' sym.SymDollar: {"$", SymClassOperator, PosPrefix}, // 39: '$'
SymHash: {"#", SymClassOperator, PosPrefix}, // 40: '#' sym.SymHash: {"#", SymClassOperator, PosPrefix}, // 40: '#'
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 41: '(' sym.SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 41: '('
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 42: ')' sym.SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 42: ')'
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 43: '[' sym.SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 43: '['
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 44: ']' sym.SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 44: ']'
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 45: '{' sym.SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 45: '{'
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 46: '}' sym.SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 46: '}'
SymTilde: {"~", SymClassOperator, PosPrefix}, // 47: '~' sym.SymTilde: {"~", SymClassOperator, PosPrefix}, // 47: '~'
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 48: '??' sym.SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 48: '??'
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 49: '?=' sym.SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 49: '?='
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 50: '?!' sym.SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 50: '?!'
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 51: '@@' sym.SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 51: '@@'
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 52: '::' sym.SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 52: '::'
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 53: '>>' sym.SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 53: '>>'
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 54: '<<' sym.SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 54: '<<'
SymCaret: {"^", SymClassOperator, PosInfix}, // 55: '^' sym.SymCaret: {"^", SymClassOperator, PosInfix}, // 55: '^'
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 56: '$(' sym.SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 56: '$('
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 57: '()' sym.SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 57: '()'
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 58: '$$' sym.SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 58: '$$'
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 59: '..' sym.SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 59: '..'
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 60: '...' sym.SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 60: '...'
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 61: '*=' sym.SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 61: '*='
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 62: '/=' sym.SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 62: '/='
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 63: '%=' sym.SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 63: '%='
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 64: '<<=' sym.SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 64: '<<='
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 65: '>>=' sym.SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 65: '>>='
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 66: '&=' sym.SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 66: '&='
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 67: '|=' sym.SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 67: '|='
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 68: '^=' sym.SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 68: '^='
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 69: '+>' sym.SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 69: '+>'
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+' sym.SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+'
SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++' sym.SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++'
SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--' sym.SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--'
SymOpenSquareLess: {"[<", SymClassOperator, PosPrefix}, // 97: '[<' sym.SymOpenSquareLess: {"[<", SymClassOperator, PosPrefix}, // 97: '[<'
SymGreaterClosedSquare: {">]", SymClassOperator, PosPostfix}, // 98: '>]' sym.SymGreaterClosedSquare: {">]", SymClassOperator, PosPostfix}, // 98: '>]'
// SymChangeSign // sym.SymChangeSign
// SymUnchangeSign // sym.SymUnchangeSign
// SymIdentifier // sym.SymIdentifier
// SymBool // sym.SymBool
// SymInteger // sym.SymInteger
// SymVariable // sym.SymVariable
// SymFloat // sym.SymFloat
// SymFraction // sym.SymFraction
// SymString // sym.SymString
// SymIterator // sym.SymIterator
// SymOr: "or", // sym.SymOr: "or",
// SymAnd: "and", // sym.SymAnd: "and",
// SymNot: "not", // sym.SymNot: "not",
// SymComment // sym.SymComment
// SymFuncCall // sym.SymFuncCall
// SymFuncDef // sym.SymFuncDef
// SymList // sym.SymList
// SymLinkedList // sym.SymLinkedList
// SymDict // sym.SymDict
// SymIndex // sym.SymIndex
// SymExpression // sym.SymExpression
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>] // sym.SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}" // sym.SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
// // SymOpenComment // 0: '/*' // // sym.SymOpenComment // 0: '/*'
// // SymClosedComment // 0: '*/' // // sym.SymClosedComment // 0: '*/'
// // SymOneLineComment // 0: '//' // // sym.SymOneLineComment // 0: '//'
// keywordBase // keywordBase
SymKwAnd: {"and", SymClassOperator, PosInfix}, sym.SymKwAnd: {"and", SymClassOperator, PosInfix},
SymKwNot: {"not", SymClassOperator, PosInfix}, sym.SymKwNot: {"not", SymClassOperator, PosInfix},
SymKwOr: {"or", SymClassOperator, PosInfix}, sym.SymKwOr: {"or", SymClassOperator, PosInfix},
SymKwBut: {"but", SymClassOperator, PosInfix}, sym.SymKwBut: {"but", SymClassOperator, PosInfix},
SymKwMap: {"map", SymClassOperator, PosInfix}, sym.SymKwMap: {"map", SymClassOperator, PosInfix},
SymKwFilter: {"filter", SymClassOperator, PosInfix}, sym.SymKwFilter: {"filter", SymClassOperator, PosInfix},
SymKwDigest: {"digest", SymClassOperator, PosInfix}, sym.SymKwDigest: {"digest", SymClassOperator, PosInfix},
SymKwCat: {"cat", SymClassOperator, PosInfix}, sym.SymKwCat: {"cat", SymClassOperator, PosInfix},
SymKwJoin: {"join", SymClassOperator, PosInfix}, sym.SymKwJoin: {"join", SymClassOperator, PosInfix},
SymKwGroupBy: {"groupby", SymClassOperator, PosInfix}, sym.SymKwGroupBy: {"groupby", SymClassOperator, PosInfix},
SymKwFunc: {"func(", SymClassDeclaration, PosPrefix}, sym.SymKwFunc: {"func(", SymClassDeclaration, PosPrefix},
SymKwBuiltin: {"builtin", SymClassOperator, PosPrefix}, sym.SymKwBuiltin: {"builtin", SymClassOperator, PosPrefix},
SymKwPlugin: {"plugin", SymClassOperator, PosPrefix}, sym.SymKwPlugin: {"plugin", SymClassOperator, PosPrefix},
SymKwIn: {"in", SymClassOperator, PosInfix}, sym.SymKwIn: {"in", SymClassOperator, PosInfix},
SymKwInclude: {"include", SymClassOperator, PosPrefix}, sym.SymKwAt: {"at", SymClassOperator, PosInfix},
SymKwNil: {"nil", SymClassValue, PosLeaf}, sym.SymKwInclude: {"include", SymClassOperator, PosPrefix},
SymKwUnset: {"unset", SymClassOperator, PosPrefix}, sym.SymKwNil: {"nil", SymClassValue, PosLeaf},
sym.SymKwUnset: {"unset", SymClassOperator, PosPrefix},
} }
} }
func SymToString(sym Symbol) string { func SymToString(sym sym.Symbol) string {
if s, ok := symbolMap[sym]; ok { if s, ok := symbolMap[sym]; ok {
return s.repr return s.repr
} }
return "" return ""
} }
func SymListToString(symList []Symbol, quote bool) string { func SymListToString(symList []sym.Symbol, quote bool) string {
var sb strings.Builder var sb strings.Builder
if len(symList) == 0 { if len(symList) == 0 {
sb.WriteString("<nothing>") sb.WriteString("<nothing>")
@@ -185,13 +188,13 @@ func SymListToString(symList []Symbol, quote bool) string {
} }
func StringEndsWithOperator(s string) bool { func StringEndsWithOperator(s string) bool {
return endingOperator(s) != SymNone return endingOperator(s) != sym.SymNone
} }
func endingOperator(s string) (sym Symbol) { func endingOperator(s string) (resultSym sym.Symbol) {
var matchLength = 0 var matchLength = 0
var repr string var repr string
sym = SymNone resultSym = sym.SymNone
lower := strings.TrimRight(strings.ToLower(s), " \t") lower := strings.TrimRight(strings.ToLower(s), " \t")
for symbol, spec := range symbolMap { for symbol, spec := range symbolMap {
if len(spec.repr) > matchLength || repr == spec.repr { if len(spec.repr) > matchLength || repr == spec.repr {
@@ -199,9 +202,9 @@ func endingOperator(s string) (sym Symbol) {
if isNotEndingSymbol(spec) && repr != spec.repr { if isNotEndingSymbol(spec) && repr != spec.repr {
repr = spec.repr repr = spec.repr
matchLength = len(spec.repr) matchLength = len(spec.repr)
sym = symbol resultSym = symbol
} else { } else {
sym = SymNone resultSym = sym.SymNone
break break
// matchLength = 0 // matchLength = 0
} }
+43 -44
View File
@@ -10,59 +10,58 @@ import (
"strings" "strings"
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/sym"
) )
func TestScanner(t *testing.T) { func TestScanner(t *testing.T) {
type inputType struct { type inputType struct {
source string source string
wantSym Symbol wantSym sym.Symbol
wantValue any wantValue any
wantErr error wantErr error
} }
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`123`, SymInteger, int64(123), nil}, /* 1 */ {`123`, sym.SymInteger, int64(123), nil},
/* 2 */ {"=", SymEqual, nil, nil}, /* 2 */ {"=", sym.SymEqual, nil, nil},
/* 3 */ {`--`, SymDoubleMinus, nil, nil}, /* 3 */ {`--`, sym.SymDoubleMinus, nil, nil},
/* 4 */ {`++`, SymDoublePlus, nil, nil}, /* 4 */ {`++`, sym.SymDoublePlus, nil, nil},
/* 5 */ {`**`, SymDoubleStar, nil, nil}, /* 5 */ {`**`, sym.SymDoubleStar, nil, nil},
/* 6 */ {`&&`, SymDoubleAmpersand, nil, nil}, /* 6 */ {`&&`, sym.SymDoubleAmpersand, nil, nil},
/* 7 */ {`||`, SymDoubleVertBar, nil, nil}, /* 7 */ {`||`, sym.SymDoubleVertBar, nil, nil},
/* 8 */ {`NOT`, SymKwNot, nil, nil}, /* 8 */ {`NOT`, sym.SymKwNot, nil, nil},
/* 9 */ {`AND`, SymKwAnd, nil, nil}, /* 9 */ {`AND`, sym.SymKwAnd, nil, nil},
/* 10 */ {`or`, SymKwOr, nil, nil}, /* 10 */ {`or`, sym.SymKwOr, nil, nil},
/* 11 */ {`+=`, SymPlusEqual, nil, nil}, /* 11 */ {`+=`, sym.SymPlusEqual, nil, nil},
/* 12 */ {`-=`, SymMinusEqual, nil, nil}, /* 12 */ {`-=`, sym.SymMinusEqual, nil, nil},
/* 13 */ {`|`, SymVertBar, nil, nil}, /* 13 */ {`|`, sym.SymVertBar, nil, nil},
/* 14 */ {`:`, SymColon, nil, nil}, /* 14 */ {`:`, sym.SymColon, nil, nil},
/* 15 */ {`;`, SymSemiColon, nil, nil}, /* 15 */ {`;`, sym.SymSemiColon, nil, nil},
/* 16 */ {`.`, SymDot, nil, nil}, /* 16 */ {`.`, sym.SymDot, nil, nil},
/* 17 */ {`0.5`, SymFloat, float64(0.5), nil}, /* 17 */ {`0.5`, sym.SymFloat, float64(0.5), nil},
/* 18 */ {`\\`, SymBackSlash, nil, nil}, /* 18 */ {`\\`, sym.SymBackSlash, nil, nil},
/* 19 */ {"`", SymBackTick, nil, nil}, /* 19 */ {"`", sym.SymBackTick, nil, nil},
/* 20 */ {"?", SymQuestion, nil, nil}, /* 20 */ {"?", sym.SymQuestion, nil, nil},
/* 21 */ {"&", SymAmpersand, nil, nil}, /* 21 */ {"&", sym.SymAmpersand, nil, nil},
/* 22 */ {"@", SymAt, nil, nil}, /* 22 */ {"@", sym.SymAt, nil, nil},
/* 23 */ {`#`, SymHash, nil, nil}, /* 23 */ {`#`, sym.SymHash, nil, nil},
/* 24 */ {`%`, SymPercent, nil, nil}, /* 24 */ {`%`, sym.SymPercent, nil, nil},
/* 25 */ {`\'`, SymQuote, nil, nil}, /* 25 */ {`\'`, sym.SymQuote, nil, nil},
/* 26 */ {`\"`, SymDoubleQuote, nil, nil}, /* 26 */ {`\"`, sym.SymDoubleQuote, nil, nil},
/* 27 */ {`_`, SymUndescore, nil, nil}, /* 27 */ {`_`, sym.SymUndescore, nil, nil},
/* 28 */ {`<>`, SymLessGreater, nil, nil}, /* 28 */ {`<>`, sym.SymLessGreater, nil, nil},
/* 29 */ {`[`, SymOpenSquare, nil, nil}, /* 29 */ {`[`, sym.SymOpenSquare, nil, nil},
/* 30 */ {`]`, SymClosedSquare, nil, nil}, /* 30 */ {`]`, sym.SymClosedSquare, nil, nil},
/* 31 */ {`{`, SymOpenBrace, nil, nil}, /* 31 */ {`{`, sym.SymOpenBrace, nil, nil},
/* 32 */ {`}`, SymClosedBrace, nil, nil}, /* 32 */ {`}`, sym.SymClosedBrace, nil, nil},
/* 33 */ {`(`, SymOpenRound, nil, nil}, /* 33 */ {`(`, sym.SymOpenRound, nil, nil},
/* 34 */ {`)`, SymClosedRound, nil, nil}, /* 34 */ {`)`, sym.SymClosedRound, nil, nil},
/* 35 */ {`1E+2`, SymFloat, float64(100), nil}, /* 35 */ {`1E+2`, sym.SymFloat, float64(100), nil},
/* 36 */ {`1E+x`, SymError, errors.New("[1:5] expected integer exponent, got x"), nil}, /* 36 */ {`1E+x`, sym.SymError, errors.New("[1:5] expected integer exponent, got x"), nil},
/* 37 */ {`$`, SymDollar, nil, nil}, /* 37 */ {`$`, sym.SymDollar, nil, nil},
/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil}, /* 38 */ {`\`, sym.SymError, errors.New("incomplete escape sequence"), nil},
/* 39 */ {`"string"`, SymString, "string", nil}, /* 39 */ {`"string"`, sym.SymString, "string", nil},
/* 40 */ {`identifier`, SymIdentifier, "identifier", nil}, /* 40 */ {`identifier`, sym.SymIdentifier, "identifier", nil},
/* 41 */ {`1.2(3)`, SymFraction, kern.NewFraction(37, 30), nil},
} }
for i, input := range inputs { for i, input := range inputs {
@@ -82,7 +81,7 @@ func TestScanner(t *testing.T) {
t.Errorf("%d: %q -> got = (nil), want %v (value %v [%T])", i+1, input.source, input.wantSym, input.wantValue, input.wantValue) t.Errorf("%d: %q -> got = (nil), want %v (value %v [%T])", i+1, input.source, input.wantSym, input.wantValue, input.wantValue)
// } else if tk.Sym != input.wantSym || tk.Value != input.wantValue { // } else if tk.Sym != input.wantSym || tk.Value != input.wantValue {
} else if tk.Sym != input.wantSym || !reflect.DeepEqual(tk.Value, input.wantValue) { } else if tk.Sym != input.wantSym || !reflect.DeepEqual(tk.Value, input.wantValue) {
if tk.Sym == SymError && input.wantSym == tk.Sym { if tk.Sym == sym.SymError && input.wantSym == tk.Sym {
if tkErr, tkOk := tk.Value.(error); tkOk { if tkErr, tkOk := tk.Value.(error); tkOk {
if inputErr, inputOk := input.wantValue.(error); inputOk { if inputErr, inputOk := input.wantValue.(error); inputOk {
if tkErr.Error() != inputErr.Error() { if tkErr.Error() != inputErr.Error() {
+6 -4
View File
@@ -4,17 +4,19 @@
// term-constructor-registry.go // term-constructor-registry.go
package scan package scan
import "git.portale-stac.it/go-pkg/expr/sym"
const initialRegistryCapacity = 10 const initialRegistryCapacity = 10
type termContructor func(tk *Token) *Term type termContructor func(tk *Token) *Term
var constructorRegistry map[Symbol]termContructor = nil var constructorRegistry map[sym.Symbol]termContructor = nil
func RegisterTermConstructor(sym Symbol, constructor termContructor) { func RegisterTermConstructor(symmbol sym.Symbol, constructor termContructor) {
if constructorRegistry == nil { if constructorRegistry == nil {
constructorRegistry = make(map[Symbol]termContructor, initialRegistryCapacity) constructorRegistry = make(map[sym.Symbol]termContructor, initialRegistryCapacity)
} }
constructorRegistry[sym] = constructor constructorRegistry[symmbol] = constructor
} }
func NewTerm(tk *Token) (inst *Term) { func NewTerm(tk *Token) (inst *Term) {
+39 -13
View File
@@ -8,13 +8,14 @@ import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/sym"
) )
type TermPriority uint32 type TermPriority uint32
const ( const (
PriNone TermPriority = iota PriNone TermPriority = iota
PriRange PriInterval
PriBut PriBut
PriAssign PriAssign
PriIterOp // map, filter, digest, etc PriIterOp // map, filter, digest, etc
@@ -160,7 +161,7 @@ func (t *Term) SetParent(parent *Term) {
} }
} }
func (t *Term) Symbol() Symbol { func (t *Term) Symbol() sym.Symbol {
return t.Tk.Sym return t.Tk.Sym
} }
@@ -186,10 +187,44 @@ func (t *Term) GetChildSource(index int) (s string) {
return return
} }
func (t *Term) IsAssign() bool { func (t *Term) GetLeftChild() (c kern.Term) {
return t.Symbol() == SymEqual if t.Position == PosInfix && len(t.Children) > 0 {
c = t.Children[0]
}
return
} }
func (t *Term) GetRightChild() (c kern.Term) {
if t.Position == PosInfix && len(t.Children) > 1 {
c = t.Children[1]
}
return
}
func (t *Term) IsSymbol(symbol sym.Symbol) bool {
return t.Symbol() == symbol
}
func (t *Term) IsOneOf(symbols ...sym.Symbol) bool {
return t.Tk.IsOneOf(symbols)
}
func (t *Term) IsAssign() bool {
return t.Symbol() == sym.SymEqual
}
func (t *Term) IsVar() bool {
return t.Symbol() == sym.SymVariable
}
func (t *Term) SetSymbol(symbol sym.Symbol) {
t.Tk.SetSymbol(symbol)
}
// func (t *Term) IsFunc() bool {
// return t.Symbol() == SymFunction
// }
func (t *Term) Compute(ctx kern.ExprContext) (v any, err error) { func (t *Term) Compute(ctx kern.ExprContext) (v any, err error) {
if t.EvalFunc == nil { if t.EvalFunc == nil {
err = t.Errorf("undefined eval-func for %q term", t.Source()) err = t.Errorf("undefined eval-func for %q term", t.Source())
@@ -199,15 +234,6 @@ func (t *Term) Compute(ctx kern.ExprContext) (v any, err error) {
return return
} }
// func (term *term) toInt(computedValue any, valueDescription string) (i int, err error) {
// if index64, ok := computedValue.(int64); ok {
// i = int(index64)
// } else {
// err = term.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
// }
// return
// }
func (t *Term) ErrIncompatibleTypes(leftValue, rightValue any) error { func (t *Term) ErrIncompatibleTypes(leftValue, rightValue any) error {
leftType := kern.TypeName(leftValue) leftType := kern.TypeName(leftValue)
leftText := kern.GetFormatted(leftValue, kern.Truncate) leftText := kern.GetFormatted(leftValue, kern.Truncate)
+24 -22
View File
@@ -8,12 +8,14 @@ import (
"fmt" "fmt"
"io" "io"
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/sym"
) )
type Token struct { type Token struct {
row int row int
col int col int
Sym Symbol Sym sym.Symbol
source string source string
Value any Value any
} }
@@ -36,19 +38,19 @@ func (tk *Token) String() string {
return tk.source return tk.source
} }
func NewToken(row, col int, sym Symbol, source string) *Token { func NewToken(row, col int, symbol sym.Symbol, source string) *Token {
return &Token{row: row, col: col, Sym: sym, source: source} return &Token{row: row, col: col, Sym: symbol, source: source}
} }
func NewValueToken(row, col int, sym Symbol, source string, value any) *Token { func NewValueToken(row, col int, symbol sym.Symbol, source string, value any) *Token {
return &Token{row: row, col: col, Sym: sym, source: source, Value: value} return &Token{row: row, col: col, Sym: symbol, source: source, Value: value}
} }
func NewErrorToken(row, col int, err error) *Token { func NewErrorToken(row, col int, err error) *Token {
if err == io.EOF { if err == io.EOF {
return NewToken(row, col, SymEos, "<EOF>") return NewToken(row, col, sym.SymEos, "<EOF>")
} }
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) return NewValueToken(row, col, sym.SymError, fmt.Sprintf("[%d:%d]", row, col), err)
} }
func (tk *Token) Source() string { func (tk *Token) Source() string {
@@ -68,31 +70,31 @@ func (tk *Token) Clone() (c *Token) {
} }
func (tk *Token) IsEos() bool { func (tk *Token) IsEos() bool {
return tk.Sym == SymEos return tk.Sym == sym.SymEos
} }
func (tk *Token) IsError() bool { func (tk *Token) IsError() bool {
return tk.Sym == SymError return tk.Sym == sym.SymError
} }
func (tk *Token) IsTerm(termSymbols []Symbol) bool { func (tk *Token) IsTerm(symbols []sym.Symbol) bool {
return tk.IsEos() || tk.IsError() || tk.IsOneOf(termSymbols) return tk.IsEos() || tk.IsError() || tk.IsOneOf(symbols)
} }
func (tk *Token) IsOneOf(termSymbols []Symbol) bool { func (tk *Token) IsOneOf(symbols []sym.Symbol) bool {
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0 return symbols != nil && slices.Index(symbols, tk.Sym) >= 0
} }
func (tk *Token) IsOneOfA(termSymbols ...Symbol) bool { func (tk *Token) IsOneOfA(symbols ...sym.Symbol) bool {
return slices.Index(termSymbols, tk.Sym) >= 0 return slices.Index(symbols, tk.Sym) >= 0
} }
func (tk *Token) IsSymbol(sym Symbol) bool { func (tk *Token) IsSymbol(symbol sym.Symbol) bool {
return tk.Sym == sym return tk.Sym == symbol
} }
func (tk *Token) SetSymbol(sym Symbol) { func (tk *Token) SetSymbol(symbol sym.Symbol) {
tk.Sym = sym tk.Sym = symbol
} }
func (tk *Token) Errorf(template string, args ...any) (err error) { func (tk *Token) Errorf(template string, args ...any) (err error) {
@@ -101,7 +103,7 @@ func (tk *Token) Errorf(template string, args ...any) (err error) {
} }
func (tk *Token) Error() (err error) { func (tk *Token) Error() (err error) {
if tk.Sym == SymError { if tk.Sym == sym.SymError {
if msg, ok := tk.Value.(error); ok { if msg, ok := tk.Value.(error); ok {
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg) err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
} }
@@ -110,7 +112,7 @@ func (tk *Token) Error() (err error) {
} }
func (tk *Token) ErrorText() (err string) { func (tk *Token) ErrorText() (err string) {
if tk.Sym == SymError { if tk.Sym == sym.SymError {
if msg, ok := tk.Value.(error); ok { if msg, ok := tk.Value.(error); ok {
err = msg.Error() err = msg.Error()
} }
@@ -137,6 +139,6 @@ func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (e
return return
} }
func (tk *Token) ErrorExpectedOneOfGot(expected ...Symbol) (err error) { func (tk *Token) ErrorExpectedOneOfGot(expected ...sym.Symbol) (err error) {
return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym)) return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym))
} }
+4 -3
View File
@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types"
"git.portale-stac.it/go-pkg/expr/util" "git.portale-stac.it/go-pkg/expr/util"
// "strings" // "strings"
) )
@@ -76,12 +77,12 @@ func (ctx *SimpleStore) Clone() kern.ExprContext {
// } // }
// } // }
func (ctx *SimpleStore) ToDict() (dict *kern.DictType) { func (ctx *SimpleStore) ToDict() any {
return kern.ContextToDict(ctx) return types.ContextToDict(ctx)
} }
func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string { func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
return kern.ContextToString(ctx, opt) return types.ContextToString(ctx, opt)
} }
// func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string { // func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
+6 -4
View File
@@ -2,7 +2,7 @@
// All rights reserved. // All rights reserved.
// symbol.go // symbol.go
package scan package sym
type Symbol int16 type Symbol int16
@@ -101,7 +101,7 @@ const (
SymList // 90: list SymList // 90: list
SymDict // 91: dict SymDict // 91: dict
SymIndex // 92: index SymIndex // 92: index
SymRange // 93: range [index : index] SymInterval // 93: range [index : index]
SymExpression // 94: expression SymExpression // 94: expression
SymSelector // 95: selector <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>] SymSelector // 95: selector <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
SymSelectorCase // 96: <selector-case> ::= [<list>] "{" <multi-expr> "}" SymSelectorCase // 96: <selector-case> ::= [<list>] "{" <multi-expr> "}"
@@ -122,6 +122,7 @@ const (
SymKwBuiltin SymKwBuiltin
SymKwPlugin SymKwPlugin
SymKwIn SymKwIn
SymKwAt
SymKwInclude SymKwInclude
SymKwMap SymKwMap
SymKwFilter SymKwFilter
@@ -133,17 +134,18 @@ const (
SymKwUnset SymKwUnset
) )
var keywords map[string]Symbol var Keywords map[string]Symbol
func init() { func init() {
//keywords = make(map[string]Symbol) //keywords = make(map[string]Symbol)
keywords = map[string]Symbol{ Keywords = map[string]Symbol{
"AND": SymKwAnd, "AND": SymKwAnd,
"BUILTIN": SymKwBuiltin, "BUILTIN": SymKwBuiltin,
"PLUGIN": SymKwPlugin, "PLUGIN": SymKwPlugin,
"BUT": SymKwBut, "BUT": SymKwBut,
"FUNC": SymKwFunc, "FUNC": SymKwFunc,
"IN": SymKwIn, "IN": SymKwIn,
"AT": SymKwAt,
"INCLUDE": SymKwInclude, "INCLUDE": SymKwInclude,
"MAP": SymKwMap, "MAP": SymKwMap,
"CAT": SymKwCat, "CAT": SymKwCat,
+94
View File
@@ -0,0 +1,94 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_array_test.go
package expr
import (
"testing"
"git.portale-stac.it/go-pkg/expr/types/array"
)
func TestArray(t *testing.T) {
section := "Array"
inputs := []inputType{
/* 1 */ {`[]`, array.NewArrayA(), nil},
/* 2 */ {`[1,2,3]`, array.NewArrayA(int64(1), int64(2), int64(3)), nil},
/* 3 */ {`[1,2,"hello"]`, array.NewArrayA(int64(1), int64(2), "hello"), nil},
/* 4 */ {`[1+2, not true, "hello"]`, array.NewArrayA(int64(3), false, "hello"), nil},
/* 5 */ {`[1,2]+[3]`, array.NewArrayA(int64(1), int64(2), int64(3)), nil},
/* 6 */ {`[1,4,3,2]-[3]`, array.NewArrayA(int64(1), int64(4), int64(2)), nil},
/* 7 */ {`builtin "math.arith"; add([1,4,3,2])`, int64(10), nil},
/* 8 */ {`builtin "math.arith"; add([1,[2,2],3,2])`, int64(10), nil},
/* 9 */ {`builtin "math.arith"; mul([1,4,3.0,2])`, float64(24.0), nil},
/* 10 */ {`builtin "math.arith"; 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`, array.ArrayFromIntsA(1, 2, 3, 4), nil},
/* 13 */ {`2-1 +> [2,3]`, array.ArrayFromIntsA(1, 2, 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},
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`},
/* 18 */ {`["a", "b", "c"]`, array.NewArrayA("a", "b", "c"), nil},
/* 19 */ {`["a", "b", "c"]`, array.NewArray([]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`, array.NewArrayA(int64(1), int64(2), int64(3)), nil},
/* 23 */ {`a=[1,2]; (a)<+3; a`, array.NewArrayA(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 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`},
/* 27 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`},
/* 28 */ {`a=[1,2]; a<+3`, array.NewArrayA(int64(1), int64(2), int64(3)), nil},
/* 29 */ {`a=[1,2]; 5+>a`, array.NewArrayA(int64(5), int64(1), int64(2)), nil},
/* 30 */ {`L=[1,2]; L[0]=9; L`, array.NewArrayA(int64(9), int64(2)), nil},
/* 31 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 32 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [array]`},
/* 33 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
/* 34 */ {`2 IN [1,2,3]`, true, nil},
/* 35 */ {`2 AT [1,2,3]`, int64(1), nil},
/* 36 */ {`4 AT [1,2,3]`, int64(-1), nil},
}
// t.Setenv("EXPR_PATH", ".")
// RunTestSuiteSpec(t, section, inputs, 43)
RunTestSuite(t, section, inputs)
}
func TestArrayInsert(t *testing.T) {
section := "Array-Insert"
inputs := []inputType{
/* 1 */ {`["a", "b"] << nil`, array.NewArrayA("a", "b"), nil},
/* 2 */ {`["a", "b"] << []`, array.NewArrayA("a", "b", array.NewArrayA()), nil},
/* 3 */ {`["a", "b"] << $([])`, array.NewArrayA("a", "b"), nil},
/* 4 */ {`["a", "b"] << 3`, array.NewArrayA("a", "b", int64(3)), nil},
/* 5 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [array] are not compatible with operator "<<"`},
/* 6 */ {`nil >> ["a", "b"]`, array.NewArrayA("a", "b"), nil},
/* 7 */ {`[] >> ["a", "b"]`, array.NewArrayA(array.NewArrayA(), "a", "b"), nil},
/* 8 */ {`$([]) >> ["a", "b"]`, array.NewArrayA("a", "b"), nil},
/* 9 */ {`["a", "b"] << $([1,2,3])`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 10 */ {`L=["a", "b"]; L << $([1,2,3])`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 11 */ {`L=["a", "b"]; L << $([1,2,3]); L`, array.NewArrayA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 12 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`},
/* 13 */ {`[0] << $([1,2,3,4])`, array.NewArrayA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
/* 14 */ {`L=[]; [1] >> L; L`, array.NewArrayA(array.ArrayFromIntsA(1)), nil},
/* 15 */ {`L=[]; L << [1]; L`, array.NewArrayA(array.ArrayFromIntsA(1)), nil},
}
// RunTestSuiteSpec(t, section, inputs, 14)
RunTestSuite(t, section, inputs)
}
func TestArrayDeepAssign(t *testing.T) {
section := "Array-DeepAssign"
inputs := []inputType{
/* 1 */ {`LL=["a", "b"]; L=LL; L[0]="x"; LL`, array.NewArrayA("x", "b"), nil},
/* 2 */ {`LL=["a", "b"]; L:=LL; L[0]="x"; LL`, array.NewArrayA("a", "b"), nil},
}
// runTestSuiteSpec(t, section, inputs, 4)
RunTestSuite(t, section, inputs)
}
+9 -8
View File
@@ -9,6 +9,7 @@ import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
) )
func TestAstString(t *testing.T) { func TestAstString(t *testing.T) {
@@ -19,9 +20,9 @@ func TestAstString(t *testing.T) {
} }
func TestAddTokensGood(t *testing.T) { func TestAddTokensGood(t *testing.T) {
tk1 := scan.NewValueToken(0, 0, scan.SymInteger, "100", 100) tk1 := scan.NewValueToken(0, 0, sym.SymInteger, "100", 100)
tk2 := scan.NewToken(0, 0, scan.SymPlus, "+") tk2 := scan.NewToken(0, 0, sym.SymPlus, "+")
tk3 := scan.NewValueToken(0, 0, scan.SymInteger, "50", 500) tk3 := scan.NewValueToken(0, 0, sym.SymInteger, "50", 500)
tree := scan.NewAst() tree := scan.NewAst()
if gotErr := tree.AddTokens(tk1, tk2, tk3); gotErr != nil { if gotErr := tree.AddTokens(tk1, tk2, tk3); gotErr != nil {
@@ -30,10 +31,10 @@ func TestAddTokensGood(t *testing.T) {
} }
func TestAddTokensBad(t *testing.T) { func TestAddTokensBad(t *testing.T) {
tk0 := scan.NewValueToken(0, 0, scan.SymInteger, "200", 200) tk0 := scan.NewValueToken(0, 0, sym.SymInteger, "200", 200)
tk1 := scan.NewValueToken(0, 0, scan.SymInteger, "100", 100) tk1 := scan.NewValueToken(0, 0, sym.SymInteger, "100", 100)
tk2 := scan.NewToken(0, 0, scan.SymPlus, "+") tk2 := scan.NewToken(0, 0, sym.SymPlus, "+")
tk3 := scan.NewValueToken(0, 0, scan.SymInteger, "50", 500) tk3 := scan.NewValueToken(0, 0, sym.SymInteger, "50", 500)
wantErr := errors.New(`[0:0] two adjacent operators: "200" and "100"`) wantErr := errors.New(`[0:0] two adjacent operators: "200" and "100"`)
@@ -44,7 +45,7 @@ func TestAddTokensBad(t *testing.T) {
} }
func TestAddUnknownTokens(t *testing.T) { func TestAddUnknownTokens(t *testing.T) {
tk0 := scan.NewToken(0, 0, scan.SymPercent, "%") tk0 := scan.NewToken(0, 0, sym.SymPercent, "%")
wantErr := errors.New(`unexpected token "%"`) wantErr := errors.New(`unexpected token "%"`)
+12 -12
View File
@@ -21,21 +21,21 @@ func TestBool(t *testing.T) {
/* 5 */ {`not "true"`, false, nil}, /* 5 */ {`not "true"`, false, nil},
/* 6 */ {`not "false"`, false, nil}, /* 6 */ {`not "false"`, false, nil},
/* 7 */ {`not ""`, true, nil}, /* 7 */ {`not ""`, true, nil},
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" does not support operand '[]' [list]`)}, /* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" does not support operand '[]' [array]`)},
/* 9 */ {`true and false`, false, nil}, /* 9 */ {`true and false`, false, nil},
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)}, /* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [array] are not compatible with operator "AND"`)},
/* 11 */ {`[] and false`, nil, errors.New(`[1:7] operator "AND" does not support operand '[]' [list] on its left side`)}, /* 11 */ {`[] and false`, nil, errors.New(`[1:7] operator "AND" does not support operand '[]' [array] on its left side`)},
/* 12 */ {`true or false`, true, nil}, /* 12 */ {`true or false`, true, nil},
/* 13 */ {`true or []`, 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`)}, /* 14 */ {`[] or false`, nil, errors.New(`got array as left operand type of 'OR' operator, it must be bool`)},
/* 15 */ {`!true`, false, nil}, /* 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"`)}, /* 13 */ //{`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [array] are not compatible with operator "OR"`)},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 13) // runTestSuiteSpec(t, section, inputs, 13)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestBoolNoShortcut(t *testing.T) { func TestBoolNoShortcut(t *testing.T) {
@@ -48,13 +48,13 @@ func TestBoolNoShortcut(t *testing.T) {
/* 5 */ {`not "true"`, false, nil}, /* 5 */ {`not "true"`, false, nil},
/* 6 */ {`not "false"`, false, nil}, /* 6 */ {`not "false"`, false, nil},
/* 7 */ {`not ""`, true, nil}, /* 7 */ {`not ""`, true, nil},
/* 8 */ {`not []`, nil, `[1:4] prefix/postfix operator "NOT" does not support operand '[]' [list]`}, /* 8 */ {`not []`, nil, `[1:4] prefix/postfix operator "NOT" does not support operand '[]' [array]`},
/* 9 */ {`true and false`, false, nil}, /* 9 */ {`true and false`, false, nil},
/* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`}, /* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [array] are not compatible with operator "AND"`},
/* 11 */ {`[] and false`, nil, `[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`}, /* 11 */ {`[] and false`, nil, `[1:7] left operand '[]' [array] and right operand 'false' [bool] are not compatible with operator "AND"`},
/* 12 */ {`true or false`, true, nil}, /* 12 */ {`true or false`, true, nil},
/* 13 */ {`true or []`, nil, `[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`}, /* 13 */ {`true or []`, nil, `[1:8] left operand 'true' [bool] and right operand '[]' [array] are not compatible with operator "OR"`},
/* 14 */ {`[] or false`, nil, `[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`}, /* 14 */ {`[] or false`, nil, `[1:6] left operand '[]' [array] and right operand 'false' [bool] are not compatible with operator "OR"`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
@@ -63,7 +63,7 @@ func TestBoolNoShortcut(t *testing.T) {
current := kern.SetCtrl(ctx, kern.ControlBoolShortcut, false) current := kern.SetCtrl(ctx, kern.ControlBoolShortcut, false)
// runCtxTestSuiteSpec(t, ctx, section, inputs, 1) // runCtxTestSuiteSpec(t, ctx, section, inputs, 1)
runCtxTestSuite(t, ctx, section, inputs) RunCtxTestSuite(t, ctx, section, inputs)
kern.SetCtrl(ctx, kern.ControlBoolShortcut, current) kern.SetCtrl(ctx, kern.ControlBoolShortcut, current)
} }
+34 -32
View File
@@ -7,7 +7,8 @@ package expr
import ( import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/types/fract"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
func TestFuncBase(t *testing.T) { func TestFuncBase(t *testing.T) {
@@ -30,25 +31,25 @@ func TestFuncBase(t *testing.T) {
/* 14 */ {`isFloat(3.1)`, true, nil}, /* 14 */ {`isFloat(3.1)`, true, nil},
/* 15 */ {`isString("3.1")`, true, nil}, /* 15 */ {`isString("3.1")`, true, nil},
/* 16 */ {`isString("3" + 1)`, true, nil}, /* 16 */ {`isString("3" + 1)`, true, nil},
/* 17 */ {`isList(["3", 1])`, true, nil}, /* 17 */ {`isArray(["3", 1])`, true, nil},
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil}, /* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
/* 19 */ {`isFract(1:3)`, true, nil}, /* 19 */ {`isFract(1:3)`, true, nil},
/* 20 */ {`isFract(3:1)`, false, nil}, /* 20 */ {`isFract(3:1)`, false, nil},
/* 21 */ {`isRational(3:1)`, true, nil}, /* 21 */ {`isRational(3:1)`, true, nil},
/* 22 */ {`fract("2.2(3)")`, kern.NewFraction(67, 30), nil}, /* 22 */ {`fract("2.2(3)")`, fract.NewFraction(67, 30), nil},
/* 23 */ {`fract("1.21(3)")`, kern.NewFraction(91, 75), nil}, /* 23 */ {`fract("1.21(3)")`, fract.NewFraction(91, 75), nil},
/* 24 */ {`fract(1.21(3))`, kern.NewFraction(91, 75), nil}, /* 24 */ {`fract(1.21(3))`, fract.NewFraction(91, 75), nil},
/* 25 */ {`fract(1.21)`, kern.NewFraction(121, 100), nil}, /* 25 */ {`fract(1.21)`, fract.NewFraction(121, 100), nil},
/* 26 */ {`dec(2)`, float64(2), nil}, /* 26 */ {`float(2)`, float64(2), nil},
/* 27 */ {`dec(2.0)`, float64(2), nil}, /* 27 */ {`float(2.0)`, float64(2), nil},
/* 28 */ {`dec("2.0")`, float64(2), nil}, /* 28 */ {`float("2.0")`, float64(2), nil},
/* 29 */ {`dec(true)`, float64(1), nil}, /* 29 */ {`float(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"}, /* 30 */ {`float(true")`, nil, "[1:13] expected one of `,`, `)`, got `\"`"},
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`}, /* 31 */ {`float()`, nil, `float(): too few params -- expected 1, got 0`},
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`}, /* 32 */ {`float(1,2,3)`, nil, `float(): too many params -- expected 1, got 3`},
/* 33 */ {`isBool(false)`, true, nil}, /* 33 */ {`isBool(false)`, true, nil},
/* 34 */ {`fract(1:2)`, kern.NewFraction(1, 2), nil}, /* 34 */ {`fract(1:2)`, fract.NewFraction(1, 2), nil},
/* 35 */ {`fract(12,2)`, kern.NewFraction(6, 1), nil}, /* 35 */ {`fract(12,2)`, fract.NewFraction(6, 1), nil},
/* 36 */ {`bool(2)`, true, nil}, /* 36 */ {`bool(2)`, true, nil},
/* 37 */ {`bool(1:2)`, true, nil}, /* 37 */ {`bool(1:2)`, true, nil},
/* 38 */ {`bool(1.0)`, true, nil}, /* 38 */ {`bool(1.0)`, true, nil},
@@ -58,9 +59,9 @@ func TestFuncBase(t *testing.T) {
/* 42 */ {`bool([])`, false, nil}, /* 42 */ {`bool([])`, false, nil},
/* 43 */ {`bool({})`, false, nil}, /* 43 */ {`bool({})`, false, nil},
/* 44 */ {`bool({1:"one"})`, true, nil}, /* 44 */ {`bool({1:"one"})`, true, nil},
/* 45 */ {`dec(false)`, float64(0), nil}, /* 45 */ {`float(false)`, float64(0), nil},
/* 46 */ {`dec(1:2)`, float64(0.5), nil}, /* 46 */ {`float(1:2)`, float64(0.5), nil},
/* 47 */ {`dec([1])`, nil, `dec(): can't convert list to float`}, /* 47 */ {`float([1])`, nil, `float(): can't convert array to float`},
/* 48 */ {`eval("a=3"); a`, int64(3), nil}, /* 48 */ {`eval("a=3"); a`, int64(3), nil},
/* 49 */ {`int(5:2)`, int64(2), nil}, /* 49 */ {`int(5:2)`, int64(2), nil},
@@ -70,7 +71,7 @@ func TestFuncBase(t *testing.T) {
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 49) // runTestSuiteSpec(t, section, inputs, 49)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFuncBaseString(t *testing.T) { func TestFuncBaseString(t *testing.T) {
@@ -88,28 +89,29 @@ func TestFuncBaseString(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 49) // runTestSuiteSpec(t, section, inputs, 49)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFuncBaseFraction(t *testing.T) { func TestFuncBaseFraction(t *testing.T) {
section := "Builtin-Base-Fraction" section := "Builtin-Base-Fraction"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`fract(-0.5)`, kern.NewFraction(-1, 2), nil}, /* 1 */ {`fract(-0.5)`, fract.NewFraction(-1, 2), nil},
/* 2 */ {`fract("")`, (*kern.FractionType)(nil), `bad syntax`}, /* 2 */ {`fract("")`, (*fract.FractionType)(nil), `bad syntax`},
/* 3 */ {`fract("-1")`, kern.NewFraction(-1, 1), nil}, /* 3 */ {`fract("-1")`, fract.NewFraction(-1, 1), nil},
/* 4 */ {`fract("+1")`, kern.NewFraction(1, 1), nil}, /* 4 */ {`fract("+1")`, fract.NewFraction(1, 1), nil},
/* 5 */ {`fract("1a")`, (*kern.FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`}, /* 5 */ {`fract("1a")`, (*fract.FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
/* 6 */ {`fract(1,0)`, nil, `fract(): division by zero`}, /* 6 */ {`fract(1,0)`, nil, `fract(): division by zero`},
/* 7 */ {`fract(5, "1")`, nil, `fract(): expected integer, got string ("1")`}, /* 7 */ {`fract(5, "1")`, nil, `fract(): expected integer, got string ("1")`},
/* 8 */ {`fract(true)`, kern.NewFraction(1, 1), nil}, /* 8 */ {`fract(true)`, fract.NewFraction(1, 1), nil},
/* 9 */ {`fract(false)`, kern.NewFraction(0, 1), nil}, /* 9 */ {`fract(false)`, fract.NewFraction(0, 1), nil},
/* 10 */ {`1.2(3)`, fract.NewFraction(37, 30), nil},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 8) // RunTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFuncBaseOthers(t *testing.T) { func TestFuncBaseOthers(t *testing.T) {
@@ -118,10 +120,10 @@ func TestFuncBaseOthers(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`set("a", 3); a`, int64(3), nil}, /* 1 */ {`set("a", 3); a`, int64(3), nil},
/* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`}, /* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`},
/* 3 */ {`seq(1,2,3)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil}, /* 3 */ {`seq(1,2,3)`, list.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
// /* 4 */ {`seq(1,2,4)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil}, // /* 4 */ {`seq(1,2,4)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
} }
// runTestSuiteSpec(t, section, inputs, 4) // RunTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+2 -2
View File
@@ -24,7 +24,7 @@ func TestFuncFmt(t *testing.T) {
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 1) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFmt(t *testing.T) { func TestFmt(t *testing.T) {
@@ -41,7 +41,7 @@ func TestFmt(t *testing.T) {
ctx := NewSimpleStore() ctx := NewSimpleStore()
currentStdout := kern.SetCtrl(ctx, kern.ControlStdout, &b) currentStdout := kern.SetCtrl(ctx, kern.ControlStdout, &b)
runCtxTestSuite(t, ctx, section, inputs) RunCtxTestSuite(t, ctx, section, inputs)
kern.SetCtrl(ctx, kern.ControlStdout, currentStdout) kern.SetCtrl(ctx, kern.ControlStdout, currentStdout)
if b.String() != text+"\n" { if b.String() != text+"\n" {
+1 -1
View File
@@ -20,5 +20,5 @@ func TestFuncImport(t *testing.T) {
t.Setenv("EXPR_PATH", "test-resources") t.Setenv("EXPR_PATH", "test-resources")
//runTestSuiteSpec(t, section, inputs, 1) //runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+85 -10
View File
@@ -5,27 +5,102 @@
package expr package expr
import ( import (
"fmt"
"io"
"os"
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/types/list"
)
const (
testFilePath = "/tmp/.go-test.txt"
) )
func TestFuncRun(t *testing.T) { func TestFuncRun(t *testing.T) {
section := "Builtin-Iterator" section := "Builtin-Iterator"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil}, /* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, int64(3), nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil}, /* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(4), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil}, /* 3 */ {`builtin "iterator"; run($(..4), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil}, /* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`}, /* 5 */ {`builtin "iterator"; run(nil)`, nil, `type of "iterator" must be iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil}, /* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, int64(3), nil},
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`}, /* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `type of "vars" must be dict, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil}, /* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, int64(3), nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`}, /* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileLineIterator("test-file.txt"); run(it)`, nil, nil}, /* 10 */ {`builtin "iterator"; op = func(index, item){ (index > 2) ? {abort("aborted")} :: {status="ok"} }; run($(..5), op)`, "aborted", nil},
/* 11 */ {`builtin "iterator"; op = func(index, item){ (index > 2) ? {abort("aborted")} :: {status="ok"} }; run($(..1), op)`, "ok", nil},
} }
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 3) // RunTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
cleanupBuiltinIterator()
}
func TestFuncOsLineIter(t *testing.T) {
section := "Builtin-OS-File-Line-Iter"
inputs := []inputType{
/* 1 */ {fmt.Sprintf(`builtin ["os.file", "iterator"]; it = fileLineIterator(%q); run(it)`, testFilePath), int64(2), nil},
/* 2 */ {fmt.Sprintf(`builtin "os.file"; it=fileLineIterator(%q); it++`, testFilePath), "uno", nil},
/* 3 */ {fmt.Sprintf(`builtin "os.file"; it=fileLineIterator(%q); it++;it++;it++`, testFilePath), nil, io.EOF.Error()},
/* 4 */ {fmt.Sprintf(`builtin "os.file"; it=fileLineIterator(%q); it.clean`, testFilePath), nil, nil},
/* 5 */ {fmt.Sprintf(`builtin "os.file"; it=fileLineIterator(%q); string(it)`, testFilePath),
fmt.Sprintf(`$(fileLineIterator@%q)`, testFilePath), nil},
/* 6 */ {fmt.Sprintf(`builtin "os.file"; #$$(fileLineIterator(%q) filter (#${_} == 2))`, testFilePath), int64(0), nil},
/* 7 */ {fmt.Sprintf(`builtin "os.file"; #$$(fileLineIterator(%q) filter (#${_} == 3))`, testFilePath), int64(2), nil},
/* 8 */ {fmt.Sprintf(`builtin "os.file"; $$(fileLineIterator(%q) map ${__})`, testFilePath), list.NewLinkedListA(0, 1), nil},
}
if err := setupBuiltinIterator(); err != nil {
t.Error(err)
return
}
// RunTestSuiteSpec(t, section, inputs, 1)
RunTestSuite(t, section, inputs)
cleanupBuiltinIterator()
}
func TestFuncOsByteIter(t *testing.T) {
section := "Builtin-OS-File-Byte-Iter"
inputs := []inputType{
/* 1 */ {fmt.Sprintf(`builtin "os.file"; it=fileByteIterator(%q); string(it)`, testFilePath),
fmt.Sprintf(`$(fileByteIterator@%q)`, testFilePath), nil},
/* 2 */ {fmt.Sprintf(`builtin ["os.file", "string"]; it=fileByteIterator(%q); char(it++)`, testFilePath), `u`, nil},
/* 3 */ {fmt.Sprintf(`builtin ["os.file", "string"]; it=fileByteIterator(%q); it++; it.reset; char(it++)`, testFilePath), `u`, nil},
}
if err := setupBuiltinIterator(); err != nil {
t.Error(err)
return
}
// t.Setenv("EXPR_PATH", ".")
// RunTestSuiteSpec(t, section, inputs, 24)
RunTestSuite(t, section, inputs)
cleanupBuiltinIterator()
}
func setupBuiltinIterator() (err error) {
var fh *os.File
// Create test file
if fh, err = os.Create(testFilePath); err == nil {
defer fh.Close()
fmt.Fprintln(fh, "uno")
fmt.Fprintln(fh, "due")
}
return
}
func cleanupBuiltinIterator() {
if fileExists(testFilePath) {
os.Remove(testFilePath)
}
} }
+1 -1
View File
@@ -29,5 +29,5 @@ func TestFuncMathArith(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 10) //runTestSuiteSpec(t, section, inputs, 10)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+23 -21
View File
@@ -5,10 +5,13 @@
package expr package expr
import ( import (
"io"
"testing" "testing"
) )
// const (
// builtinOsFile_dummyFile = "/tmp/.go-test_dummy.txt"
// )
func TestFuncOs(t *testing.T) { func TestFuncOs(t *testing.T) {
section := "Builtin-OS-File" section := "Builtin-OS-File"
inputs := []inputType{ inputs := []inputType{
@@ -27,30 +30,29 @@ func TestFuncOs(t *testing.T) {
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`}, /* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil}, /* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`}, /* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
/* 16 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it++`, "uno", nil}, /* 16 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle); string(handle)`, `reader`, nil},
/* 17 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()}, /* 17 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle); string(handle)`, `writer`, nil},
/* 18 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it.clean`, nil, nil},
/* 19 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); string(it)`, `$(fileLineIterator@"test-file.txt")`, nil},
/* 20 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle); string(handle)`, `reader`, nil},
/* 21 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle); string(handle)`, `writer`, nil},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 24) // RunTestSuiteSpec(t, section, inputs, 17)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFuncOsByteIter(t *testing.T) { // func setupBuiltinOsFile() (err error) {
section := "Builtin-OS-File-Byte-Iter" // var fh *os.File
inputs := []inputType{ // // Create test file
/* 1 */ {`builtin "os.file"; it=fileByteIterator("test-file.txt"); string(it)`, `$(fileByteIterator@"test-file.txt")`, nil}, // if fh, err = os.Create(builtinOsFile_dummyFile); err == nil {
/* 2 */ {`builtin ["os.file", "string"]; it=fileByteIterator("test-file.txt"); char(it++)`, `u`, nil}, // defer fh.Close()
/* 3 */ {`builtin ["os.file", "string"]; it=fileByteIterator("test-file.txt"); it++; it.reset; char(it++)`, `u`, nil}, // fmt.Fprintln(fh, "uno")
} // fmt.Fprintln(fh, "due")
// }
// return
// }
// t.Setenv("EXPR_PATH", ".") // func cleanupBuiltinOsFile() {
// if fileExists(builtinOsFile_dummyFile) {
// runTestSuiteSpec(t, section, inputs, 24) // os.Remove(builtinOsFile_dummyFile)
runTestSuite(t, section, inputs) // }
} // }
+4 -4
View File
@@ -7,7 +7,7 @@ package expr
import ( import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/types/array"
) )
func TestFuncString(t *testing.T) { func TestFuncString(t *testing.T) {
@@ -30,7 +30,7 @@ func TestFuncString(t *testing.T) {
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil}, /* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil}, /* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`}, /* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`},
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, kern.NewListA("one", "two", "three"), nil}, /* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, array.NewArrayA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`}, /* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`}, /* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
/* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil}, /* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil},
@@ -52,7 +52,7 @@ func TestFuncString(t *testing.T) {
isFloat(any) -> boolean, isFloat(any) -> boolean,
isFract(any) -> boolean, isFract(any) -> boolean,
isInt(any) -> boolean, isInt(any) -> boolean,
isList(any) -> boolean, isArray(any) -> boolean,
isNil(any) -> boolean, isNil(any) -> boolean,
isString(any) -> boolean, isString(any) -> boolean,
joinStr(separator, item="" ...) -> string, joinStr(separator, item="" ...) -> string,
@@ -68,5 +68,5 @@ func TestFuncString(t *testing.T) {
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 19) // runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+50 -11
View File
@@ -6,6 +6,9 @@ package expr
import ( import (
"errors" "errors"
"os"
"path"
"runtime"
"strings" "strings"
"testing" "testing"
@@ -19,9 +22,27 @@ type inputType struct {
wantErr any wantErr any
} }
func runCtxTestSuiteSpec(t *testing.T, ctx kern.ExprContext, section string, inputs []inputType, spec ...int) { func getSourceName(startFrame int) (sourcename string) {
for i := startFrame; i < 10; i++ {
_, sourcepath, _, ok := runtime.Caller(i)
if !ok {
break
}
sourcename = path.Base(sourcepath)
if sourcename != "t_common_test.go" {
break
}
}
return
}
func RunCtxTestSuiteSpec(t *testing.T, ctx kern.ExprContext, section string, inputs []inputType, spec ...int) {
succeeded := 0 succeeded := 0
failed := 0 failed := 0
sourcename := getSourceName(2)
t.Logf("Test suite %s from file %s", section, sourcename)
for _, count := range spec { for _, count := range spec {
index := count - 1 index := count - 1
if index >= 0 && index < len(inputs) { if index >= 0 && index < len(inputs) {
@@ -40,15 +61,19 @@ func runCtxTestSuiteSpec(t *testing.T, ctx kern.ExprContext, section string, inp
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed) t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
} }
func runTestSuiteSpec(t *testing.T, section string, inputs []inputType, spec ...int) { func RunTestSuiteSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
runCtxTestSuiteSpec(t, nil, section, inputs, spec...) RunCtxTestSuiteSpec(t, nil, section, inputs, spec...)
} }
func runCtxTestSuite(t *testing.T, ctx kern.ExprContext, section string, inputs []inputType) { func RunCtxTestSuite(t *testing.T, ctx kern.ExprContext, section string, inputs []inputType) {
succeeded := 0 succeeded := 0
failed := 0 failed := 0
sourcename := getSourceName(2)
t.Logf("Test suite %s from file %s", section, sourcename)
for i, input := range inputs { for i, input := range inputs {
// fmt.Printf("%3d: %s\n", i+1, input.source) // fmt.Printf("%3d: %s\n", i+1, input.source)
@@ -61,8 +86,8 @@ func runCtxTestSuite(t *testing.T, ctx kern.ExprContext, section string, inputs
} }
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
} }
func runTestSuite(t *testing.T, section string, inputs []inputType) { func RunTestSuite(t *testing.T, section string, inputs []inputType) {
runCtxTestSuite(t, nil, section, inputs) RunCtxTestSuite(t, nil, section, inputs)
} }
func getWantedError(input *inputType) error { func getWantedError(input *inputType) error {
@@ -98,14 +123,21 @@ func doTest(t *testing.T, ctx kern.ExprContext, section string, input *inputType
gotResult, gotErr = ast.Eval(ctx) gotResult, gotErr = ast.Eval(ctx)
} }
eq := kern.Equal(gotResult, input.wantResult) if gotErr == nil {
if wantErr == nil {
eq := kern.Equal(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ { if !eq /*gotResult != input.wantResult*/ {
t.Errorf(">>>%s/%d: `%s` -> result = %v [%s], want = %v [%s]", section, count, input.source, gotResult, kern.TypeName(gotResult), input.wantResult, kern.TypeName(input.wantResult)) t.Errorf(">>>%s/%d: `%s` -> result = %v [%s], want = %v [%s]", section, count, input.source, gotResult, kern.TypeName(gotResult), input.wantResult, kern.TypeName(input.wantResult))
good = false good = false
}
} else {
t.Errorf(">>>%s/%d: `%s` -> result = %v [%s], got-err = <nil>, expected-err = %v", section, count, input.source, gotResult, kern.TypeName(gotResult), wantErr)
good = false
}
} }
if gotErr != wantErr { if good && gotErr != wantErr {
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) { if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf(">>>%s/%d: %s -> got-err = <%v>, expected-err = <%v>", section, count, input.source, gotErr, wantErr) t.Errorf(">>>%s/%d: %s -> got-err = <%v>, expected-err = <%v>", section, count, input.source, gotErr, wantErr)
good = false good = false
@@ -145,3 +177,10 @@ func testIteratorCallOp(t *testing.T, section string, it kern.Iterator) {
t.Errorf(`%s -- CallOperation("fake") should return error`, section) t.Errorf(`%s -- CallOperation("fake") should return error`, section)
} }
} }
// Test utilities
func fileExists(filePath string) bool {
_, err := os.Stat(filePath)
return !errors.Is(err, os.ErrNotExist)
}
+4 -4
View File
@@ -7,7 +7,7 @@ package expr
import ( import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/types/list"
) )
func TestCtrlSet(t *testing.T) { func TestCtrlSet(t *testing.T) {
@@ -82,12 +82,12 @@ func TestList(t *testing.T) {
section := "Context" section := "Context"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`$$(5)`, kern.NewLinkedListA(5), nil}, /* 1 */ {`$$(5)`, list.NewLinkedListA(5), nil},
/* 2 */ {`$$($(2))`, kern.NewLinkedListA(0, 1), nil}, /* 2 */ {`$$($(..2))`, list.NewLinkedListA(0, 1), nil},
/* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil}, /* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil},
} }
// runTestSuiteSpec(t, section, inputs, 3) // runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+51 -20
View File
@@ -11,44 +11,63 @@ import (
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan" "git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/sym"
"git.portale-stac.it/go-pkg/expr/types/dict"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
func TestDictParser(t *testing.T) { func TestDictParser(t *testing.T) {
section := "Dict" section := "Dict"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`{}`, kern.NewDict(nil), nil}, /* 1 */ {`{}`, dict.NewDict(nil), nil},
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")}, /* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
/* 3 */ {`{1:"one",2:"two",3:"three"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil}, /* 3 */ {`{1:"one",2:"two",3:"three"}`, dict.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil},
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil}, /* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil}, /* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
/* 6 */ {`{1:"one"} + {2:"two"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil}, /* 6 */ {`{1:"one"} + {2:"two"}`, dict.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil},
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil}, /* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, kern.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil}, /* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, dict.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil},
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, kern.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil}, /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, dict.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)}, /* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil}, /* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
/* 12 */ {`m={ /* 12 */ {`m={
"a":1, "a":1,
//"b":2, //"b":2,
"c":3 "c":3
}`, kern.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil}, }`, dict.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil},
/* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil}, /* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil},
/* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil}, /* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil},
/* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil}, /* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil},
/* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`}, /* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`},
/* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`}, /* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`},
/* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil}, /* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil},
/* 19 */ {`k="a"; D={k: 10}`, kern.NewDict(map[any]any{"a": int64(10)}), nil}, /* 19 */ {`k="a"; D={k: 10}`, dict.NewDict(map[any]any{"a": int64(10)}), nil},
/* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`}, /* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`},
/* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`}, /* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`},
/* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, kern.NewDict(map[any]any{"x5": int64(10)}), nil}, /* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, dict.NewDict(map[any]any{"x5": int64(10)}), nil},
/* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of `:`, `}`, got `;`"}, /* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of `:`, `}`, got `;`"},
/* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, kern.NewDict(map[any]any{"z": int64(10)}), nil}, /* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, dict.NewDict(map[any]any{"z": int64(10)}), nil},
/* 25 */ {`3 in {1:"one", 2:"two"}`, false, nil},
/* 26 */ {`1 at {"a":2, "b":3, "c":1}`, "c", nil},
/* 27 */ {`10 at {"a":2, "b":3, "c":1}`, nil, nil},
} }
// runTestSuiteSpec(t, section, inputs, 23, 24) // RunTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
}
func TestOperations(t *testing.T) {
section := "Dict-Operations"
inputs := []inputType{
/* 1 */ {`{"a":1} + {"b":2}`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
/* 2 */ {`D={"a":1}; C={"b":2}; X=D+C; C."b" = 3; X`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
/* 3 */ {`D={"a":1}; C={"b":2}; X=D+C; D."a" = 3; X`, dict.NewDict(map[any]any{"a": int64(1), "b": int64(2)}), nil},
/* 4 */ {`L=[8,9]; D={"a":L}; L[0]=11; D."a"[0]`, int64(11), nil},
/* 5 */ {`L=[8,9]; D:={"a":L}; L[0]=11; D."a"[0]`, int64(8), nil},
}
// runTestSuiteSpec(t, section, inputs, 1)
RunTestSuite(t, section, inputs)
} }
func TestAccessSubFields(t *testing.T) { func TestAccessSubFields(t *testing.T) {
@@ -56,9 +75,9 @@ func TestAccessSubFields(t *testing.T) {
ctx := NewSimpleStore() ctx := NewSimpleStore()
ctx.UnsafeSetVar( ctx.UnsafeSetVar(
"D", "D",
kern.NewDict(map[any]any{ dict.NewDict(map[any]any{
"a": kern.NewDict(map[any]any{"uno": int64(10)}), "a": dict.NewDict(map[any]any{"uno": int64(10)}),
"b": kern.NewListA(1, 2, 3), "b": list.NewLinkedListA(1, 2, 3),
}), }),
) // D={"a": {"uno":1}} ) // D={"a": {"uno":1}}
@@ -67,9 +86,21 @@ func TestAccessSubFields(t *testing.T) {
/* 2 */ {`D.a.uno = 111; D.a."uno"`, int64(111), nil}, /* 2 */ {`D.a.uno = 111; D.a."uno"`, int64(111), nil},
/* 3 */ {`D.a.uno = 111; D["a"]["uno"]`, int64(111), nil}, /* 3 */ {`D.a.uno = 111; D["a"]["uno"]`, int64(111), nil},
/* 4 */ {`D.b[1] = 22; D["b"][1]`, int64(22), nil}, /* 4 */ {`D.b[1] = 22; D["b"][1]`, int64(22), nil},
// /* 4 */ {`D.b[1] = 22; D["b"][1]`, nil, `[1:3] collection expected`},
} }
// runCtxTestSuiteSpec(t, ctx, section, inputs, 4) RunCtxTestSuiteSpec(t, ctx, section, inputs, 4)
runCtxTestSuite(t, ctx, section, inputs) // runCtxTestSuite(t, ctx, section, inputs)
}
func TestDictDeepAssign(t *testing.T) {
section := "Dict-DeepAssign"
inputs := []inputType{
/* 1 */ {`DD={"uno": 1, "due": 2}; D=DD; D["uno"]=11; DD`, dict.NewDict(map[any]any{"uno": int64(11), "due": int64(2)}), nil},
/* 2 */ {`DD={"uno": 1, "due": 2}; D:=DD; D["uno"]=11; DD`, dict.NewDict(map[any]any{"uno": int64(1), "due": int64(2)}), nil},
}
// runTestSuiteSpec(t, section, inputs, 4)
RunTestSuite(t, section, inputs)
} }
func TestDictToStringMultiLine(t *testing.T) { func TestDictToStringMultiLine(t *testing.T) {
@@ -79,9 +110,9 @@ func TestDictToStringMultiLine(t *testing.T) {
"first": 1 "first": 1
}` }`
args := map[any]any{ args := map[any]any{
"first": newLiteralTerm(scan.NewValueToken(0, 0, scan.SymInteger, "1", 1)), "first": newLiteralTerm(scan.NewValueToken(0, 0, sym.SymInteger, "1", 1)),
} }
dict := kern.NewDict(args) dict := dict.NewDict(args)
got := dict.ToString(kern.MultiLine) got := dict.ToString(kern.MultiLine)
// fmt.Printf("got=%q\n", got) // fmt.Printf("got=%q\n", got)
@@ -101,9 +132,9 @@ func TestDictToString(t *testing.T) {
section := "dict-ToString-SL" section := "dict-ToString-SL"
want := `{"first": 1}` want := `{"first": 1}`
args := map[any]any{ args := map[any]any{
"first": newLiteralTerm(scan.NewValueToken(0, 0, scan.SymInteger, "1", 1)), "first": newLiteralTerm(scan.NewValueToken(0, 0, sym.SymInteger, "1", 1)),
} }
dict := kern.NewDict(args) dict := dict.NewDict(args)
got := dict.ToString(0) got := dict.ToString(0)
// fmt.Printf("got=%q\n", got) // fmt.Printf("got=%q\n", got)
+23 -22
View File
@@ -7,7 +7,7 @@ package expr
import ( import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/types/dict"
) )
func TestExpr(t *testing.T) { func TestExpr(t *testing.T) {
@@ -16,23 +16,26 @@ func TestExpr(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`0?{}`, nil, nil}, /* 1 */ {`0?{}`, nil, nil},
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil}, /* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil}, /* 3 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 4 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`a=3; a+=1; a`, int64(4), nil},
/* 6 */ {`a=3; a+=1; a`, int64(4), nil}, /* 6 */ {`a=3; a-=1; a`, int64(2), nil},
/* 7 */ {`a=3; a-=1; a`, int64(2), nil}, /* 7 */ {`a=3; a*=2; a`, int64(6), nil},
/* 8 */ {`a=3; a*=2; a`, int64(6), nil}, /* 8 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil}, /* 9 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil}, /* 10 */ {`a=3; a/=2`, int64(1), nil},
/* 11 */ {`a=3; a/=2`, int64(1), nil}, /* 11 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`}, /* 12 */ {`a=3; a*=2+1; a`, int64(9), nil},
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil}, /* 13 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil}, /* 14 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`}, /* 15 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil}, /* 16 */ {`true ? {"a"} :: {"b"}`, "a", nil},
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil}, /* 17 */ {`2 ? {"a"}`, nil, `[1:2] no case catches the value (2) of the selection expression`},
/* 18 */ {`$$`, kern.NewDict(map[any]any{"vars": kern.NewDict(nil), "funcs": kern.NewDict(nil)}), nil}, /* 18 */ {`2 ? 5`, nil, "[1:6] expected `{`, got `5`"},
/* 19 */ {` /* 19 */ {`a=2; ${a}`, int64(2), nil},
/* 20 */ {`$_=2; $_`, int64(2), nil},
/* 21 */ {`$$`, dict.NewDict(map[any]any{"vars": dict.NewDict(nil), "funcs": dict.NewDict(nil)}), nil},
/* 22 */ {`
ds={ ds={
"init":func(@end){@current=0 but true}, "init":func(@end){@current=0 but true},
//"current":func(){current}, //"current":func(){current},
@@ -44,11 +47,9 @@ func TestExpr(t *testing.T) {
it++; it++;
it++ it++
`, int64(1), nil}, `, int64(1), nil},
/* 20 */ {`a=2; ${a}`, int64(2), nil},
/* 21 */ {`$_=2; $_`, int64(2), nil},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 21) // RunTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
+16 -15
View File
@@ -8,37 +8,38 @@ import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/types/fract"
) )
func TestFractionsParser(t *testing.T) { func TestFractionsParser(t *testing.T) {
section := "Fraction" section := "Fraction"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`1:2`, kern.NewFraction(1, 2), nil}, /* 1 */ {`1:2`, fract.NewFraction(1, 2), nil},
/* 2 */ {`1:2 + 1`, kern.NewFraction(3, 2), nil}, /* 2 */ {`1:2 + 1`, fract.NewFraction(3, 2), nil},
/* 3 */ {`1:2 - 1`, kern.NewFraction(-1, 2), nil}, /* 3 */ {`1:2 - 1`, fract.NewFraction(-1, 2), nil},
/* 4 */ {`1:2 * 1`, kern.NewFraction(1, 2), nil}, /* 4 */ {`1:2 * 1`, fract.NewFraction(1, 2), nil},
/* 5 */ {`1:2 * 2:3`, kern.NewFraction(2, 6), nil}, /* 5 */ {`1:2 * 2:3`, fract.NewFraction(2, 6), nil},
/* 6 */ {`1:2 / 2:3`, kern.NewFraction(3, 4), nil}, /* 6 */ {`1:2 / 2:3`, fract.NewFraction(3, 4), nil},
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`}, /* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`}, /* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
/* 9 */ {`1:+5`, kern.NewFraction(1, 5), nil}, /* 9 */ {`1:+5`, fract.NewFraction(1, 5), nil},
/* 10 */ {`1:(-2)`, kern.NewFraction(-1, 2), nil}, /* 10 */ {`1:(-2)`, fract.NewFraction(-1, 2), nil},
/* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, kern.NewFraction(7, 6), nil}, /* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, fract.NewFraction(7, 6), nil},
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil}, /* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, kern.NewFraction(2, 6), nil}, /* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, fract.NewFraction(2, 6), nil},
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil}, /* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
/* 15 */ {`1:0`, nil, `[1:3] division by zero`}, /* 15 */ {`1:0`, nil, `[1:3] division by zero`},
/* 16 */ {`1+1:2+0.5`, float64(2), nil}, /* 16 */ {`1+1:2+0.5`, float64(2), nil},
/* 17 */ {`1:(2-2)`, nil, `[1:3] division by zero`}, /* 17 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
/* 18 */ {`[0,1][1-1]:1`, kern.NewFraction(0, 1), nil}, /* 18 */ {`[0,1][1-1]:1`, fract.NewFraction(0, 1), nil},
/* 19 */ {`1:2 == 0.5`, true, nil}, /* 19 */ {`1:2 == 0.5`, true, nil},
} }
// runTestSuiteSpec(t, section, inputs, 26) // runTestSuiteSpec(t, section, inputs, 26)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFractionToStringSimple(t *testing.T) { func TestFractionToStringSimple(t *testing.T) {
source := kern.NewFraction(1, 2) source := fract.NewFraction(1, 2)
want := "1:2" want := "1:2"
got := source.ToString(0) got := source.ToString(0)
if got != want { if got != want {
@@ -47,7 +48,7 @@ func TestFractionToStringSimple(t *testing.T) {
} }
func TestFractionToStringMultiline(t *testing.T) { func TestFractionToStringMultiline(t *testing.T) {
source := kern.NewFraction(1, 2) source := fract.NewFraction(1, 2)
want := "1\n-\n2" want := "1\n-\n2"
got := source.ToString(kern.MultiLine) got := source.ToString(kern.MultiLine)
if got != want { if got != want {
@@ -56,7 +57,7 @@ func TestFractionToStringMultiline(t *testing.T) {
} }
func TestToStringMultilineTty(t *testing.T) { func TestToStringMultilineTty(t *testing.T) {
source := kern.NewFraction(-1, 2) source := fract.NewFraction(-1, 2)
want := "\x1b[4m-1\x1b[0m\n 2" want := "\x1b[4m-1\x1b[0m\n 2"
got := source.ToString(kern.MultiLine | kern.TTY) got := source.ToString(kern.MultiLine | kern.TTY)
if got != want { if got != want {
+3 -3
View File
@@ -46,7 +46,7 @@ func TestFuncs(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 19) //runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func TestFuncs2(t *testing.T) { func TestFuncs2(t *testing.T) {
@@ -57,7 +57,7 @@ func TestFuncs2(t *testing.T) {
/* 3 */ {`builtin "iterator"; times=func(a,b){a*b}; run($(["1", "2", "3"]), times)`, nil, `operator(): missing params -- a, b`}, /* 3 */ {`builtin "iterator"; times=func(a,b){a*b}; run($(["1", "2", "3"]), times)`, nil, `operator(): missing params -- a, b`},
} }
// runTestSuiteSpec(t, section, inputs, 4) // runTestSuiteSpec(t, section, inputs, 4)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }
func dummy(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) { func dummy(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
@@ -102,6 +102,6 @@ func TestGoFunction(t *testing.T) {
kern.NewFuncParamFlagDef("name", kern.PfOptional|kern.PfDefault, "Celestino Amoroso"), kern.NewFuncParamFlagDef("name", kern.PfOptional|kern.PfDefault, "Celestino Amoroso"),
}) })
runCtxTestSuite(t, ctx, section, inputs) RunCtxTestSuite(t, ctx, section, inputs)
} }
+35 -11
View File
@@ -7,24 +7,48 @@ package expr
import ( import (
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern" "git.portale-stac.it/go-pkg/expr/types/array"
"git.portale-stac.it/go-pkg/expr/types/list"
) )
func TestCollections(t *testing.T) { func TestCollections(t *testing.T) {
section := "Index" section := "Index"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`"abcdef"[1:3]`, "bc", nil}, /* 1 */ {`"abcdef"[1..3]`, "bc", nil},
/* 2 */ {`"abcdef"[:3]`, "abc", nil}, /* 2 */ {`"abcdef"[..3]`, "abc", nil},
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil}, /* 3 */ {`"abcdef"[1..]`, "bcdef", nil},
/* 4 */ {`"abcdef"[:]`, "abcdef", nil}, /* 4 */ {`"abcdef"[..]`, "abcdef", nil},
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`}, /* 5 */ {`"abcdef"[1..2..3]`, `b`, nil},
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil}, /* 6 */ {`"abcdef"[((1>0)?{1}:{0})..3]`, "bc", nil},
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil}, /* 7 */ {`"abcdef"[[0,1][0]..1]`, "a", nil},
/* 8 */ {`[0,1,2,3,4][:]`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, /* 8 */ {`"abcdef"[..1]`, "a", nil},
/* 9 */ {`"abcdef"[..-1]`, "abcde", nil},
/* 10 */ {`"abcdef"[..-1..2]`, "ace", nil},
/* 11 */ {`"abcdef"[-3..-1..2]`, "d", nil},
/* 12 */ {`"abcdef"[3..0]`, "dcba", nil},
/* 13 */ {`"abcdef"[-1..0..2]`, "fdb", nil},
/* 14 */ {`[0,1,2,3,4][..]`, array.NewIntArrayA(0, 1, 2, 3, 4), nil},
/* 15 */ {`[0,1,2,3,4][2..3]`, array.NewIntArrayA(2), nil},
/* 16 */ {`[0,1,2,3,4][3..-1]`, array.NewIntArrayA(3), nil},
/* 17 */ {`[0,1,2,3,4][-3..-1]`, array.NewIntArrayA(2, 3), nil},
/* 18 */ {`[0,1,2,3,4][0..]`, array.NewIntArrayA(0, 1, 2, 3, 4), nil},
/* 19 */ {`[0,1,2,3,4][0..-1..2]`, array.NewIntArrayA(0, 2), nil},
/* 20 */ {`[0,1,2,3,4][-1..0..2]`, array.NewIntArrayA(4, 2), nil},
/* 21 */ {`[<0,1,2,3,4>][..]`, list.NewLinkedListA(0, 1, 2, 3, 4), nil},
/* 22 */ {`[<0,1,2,3,4>][2..3]`, list.NewLinkedListA(2), nil},
/* 23 */ {`[<0,1,2,3,4>][3..-1]`, list.NewLinkedListA(3), nil},
/* 24 */ {`[<0,1,2,3,4>][-3..-1]`, list.NewLinkedListA(2, 3), nil},
/* 25 */ {`[<0,1,2,3,4>][0..]`, list.NewLinkedListA(0, 1, 2, 3, 4), nil},
/* 26 */ {`[<0,1,2,3,4>][0..-1..2]`, list.NewLinkedListA(0, 2), nil},
/* 27 */ {`[<0,1,2,3,4>][-1..0..2]`, list.NewLinkedListA(4, 2), nil},
/* 28 */ {`[-1..0..2]`, nil, `[1:8] unexpected interval expression`},
/* 29 */ {`"abcdef"['x'..]`, nil, `[1:14] interval expression expected integer, got string (x)`},
/* 30 */ {`"abcdef"[1+..]`, nil, `[1:12] infix operator "+" requires two non-nil operands, got 1`},
/* 31 */ {`"abcdef"[1..4+]`, nil, `[1:15] infix operator "+" requires two non-nil operands, got 1`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 5) // RunTestSuiteSpec(t, section, inputs, 30)
runTestSuite(t, section, inputs) RunTestSuite(t, section, inputs)
} }

Some files were not shown because too many files have changed in this diff Show More