Compare commits

...

24 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 dba8e01aa0 ecli.doc: correction and improvement 2026-06-06 06:14:58 +02:00
camoroso 6a6c897268 first ecli commit 2026-06-03 08:59:48 +02:00
camoroso 9eb3e2d072 Merge branch 'main' into all-in-one 2026-06-03 06:38:42 +02:00
camoroso cd8f331a32 Merge branch 'main' into all-in-one 2026-05-22 06:47:50 +02:00
camoroso 7ed5a806f9 changed import conventions for plugin names: expr-<scoped-name>; scoped-name = <scope>-<name> 2026-05-22 05:59:46 +02:00
144 changed files with 4987 additions and 2118 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
} }
+14
View File
@@ -0,0 +1,14 @@
# Builder resource file
# Created on gio 21 mag 2026, 15:35:18, CEST
# Program name
PROGRAM_NAME="ecli"
# Program version
PROGRAM_VERSION="$(<version.txt)"
# Preset platform
platform=("linux/amd64" "darwin/arm64")
#--- end of file ---
+199
View File
@@ -0,0 +1,199 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
# All rights reserved.
RESOURCE_FILE=".build.rc"
BUILD_REGISTER=".build_register"
PROGRAM_NAME=""
PROGRAM_VERSION=""
function usage() {
prog=$(basename "${0}")
msgln "USAGE:"
#msgln " ${prog} <program-name> <program-version> <os>/<platform>..."
msgln " ${prog} <os>/<platform>..."
msgln " ${prog} --local Build the local exec"
msgln " ${prog} --init Create the resource file in the current directory"
msgln
if [ -r "${RESOURCE_FILE}" ]; then
msgln "Resource file '${RESOURCE_FILE}' content:"
cat >&2 ${RESOURCE_FILE}
else
msgln "Resource file '${RESOURCE_FILE}' not found"
fi
}
function msgln() {
echo >&2 "${1}"
}
function exitUsage() {
echo >&2 "${1}"
usage
exit 1
}
function exitMsg() {
echo >&2 "${1}"
exit 1
}
# CMDLINE: help
if [ "${1}" == "help,," ] || [ "${1,,}" == "--help" ] || [ "${1,,}" == "-h" ]; then
usage
exit
fi
# CMDLINE: init
if [ "${1,,}" == "--init" ]; then
cat >"${RESOURCE_FILE}" <<eot
# Builder resource file
# Created on $(date)
# Program name
PROGRAM_NAME="name"
# Program version
PROGRAM_VERSION="version"
# Preset platform
platform=("linux/amd64" "darwin/arm64")
#--- end of file ---
eot
msgln "Resource file '${RESOURCE_FILE}' create. Edit it to set valid values."
#${EDITOR-vi} "${RESOURCE_FILE}"
exit
fi
if [ -r "${RESOURCE_FILE}" ]; then
if ! source "${RESOURCE_FILE}"; then
exitMsg "Can't load build resource file '${RESOURCE_FILE}'"
fi
fi
if [ -z "${PROGRAM_NAME}" ]; then
exitUsage "Missing program name"
fi
if [ -z "${PROGRAM_VERSION}" ]; then
exitUsage "Missing program version"
fi
function getBuildCount() {
local reg ver count
if [ -r "${BUILD_REGISTER}" ]; then
reg=$(<"${BUILD_REGISTER}")
else
reg="${PROGRAM_VERSION} 0"
fi
read ver count <<<"${reg}"
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
count=0
fi
count=$((count+1))
echo >"${BUILD_REGISTER}" "${PROGRAM_VERSION} ${count}"
echo ${count}
}
function build() {
local target=${1} ext cmd
IFS=/ read os cpu <<<"${p}"
#msgln "OS=${os}; CPU=${cpu}"
ext=""
if [ "${os}" == 'windows' ]; then
ext=".exe"
fi
cmd="GOOS='${os}' GOARCH='${cpu}' go build -o '${PROGRAM_NAME}_v${PROGRAM_VERSION}_${os}_${cpu}${ext}'"
eval "${cmd}"
}
function buildLocal() {
local ext cmd
ext=""
if [[ "${OSTYPE}" =~ win.* ]]; then
ext=".exe"
fi
cmd="go build -o '${PROGRAM_NAME}${ext}'"
eval "${cmd}"
}
function gitTag() {
local gopath gopkg mod
local tag
if ! tag=$(git tag -l --sort=-version:refname "v[0-9]*.[0-9]*.[0-9]*"|head -1) || [ -z "${tag}" ]; then
gopath=$(go env GOPATH)
gopkg="${gopath}/pkg/mod/git.portale-stac.it/go-pkg"
if cd "${gopkg}" 2>/dev/null; then
mod=$(ls -1v |grep expr@|tail -1)
tag=${mod##*@}
cd - >/dev/null
fi
fi
echo ${tag}
}
function gitTagDate() {
local tag_name=${1}
local tag_date
if ! tag_date=$(git show --no-patch --format=%ci "${tag_name}") || [ -z "${tag_date}" ]; then
tag_date="n/a"
fi
echo ${tag_date}
}
function createVersionSource() {
local tag tag_date
tag=$(gitTag)
if [ -z "${tag}" ]; then
tag="n/a"
else
tag_date=$(gitTagDate "${tag}")
fi
cat >version.go <<eot
// Copyright (c) 2024-$(date +%Y) Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// version.go
package main
const (
PROGNAME = "${PROGRAM_NAME}"
VERSION = "v${PROGRAM_VERSION}(build $(getBuildCount)),$(date +"%Y/%m/%d") (celestino.amoroso@portale-stac.it)"
EXPR_VERSION = "${tag}"
EXPR_DATE = "${tag_date}"
)
eot
}
## -- TEST -- ##
# echo "Tag: $(gitTag)"
# echo "Tag Date: $(gitTagDate $(gitTag))"
# exit 0
## -- MAIN -- ##
createVersionSource
if [ "${1}" == "--local" ]; then
buildLocal
exit
fi
if [ ${#} -gt 0 ]; then
for p; do
build "${p}"
done
else
for p in ${platform[@]}; do
build "${p}"
done
fi
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
# All rights reserved
RESOURCE_FILE=".build.rc"
BUILD_REGISTER=".build_register"
PASSWORD=
GITEA_USER="camoroso"
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
GITEA_OWNER="go-pkg"
GITEA_HOST="https://git.portale-stac.it"
GITEA_BASE_PATH="api/v1/packages"
GITEA_PKG_TYPE="generic"
function msgln() {
echo >&2 "${1}"
}
function exitMsg() {
echo >&2 "${1}"
exit 1
}
function readBuildCount() {
local reg ver count
if [ -r "${BUILD_REGISTER}" ]; then
reg=$(<"${BUILD_REGISTER}")
else
reg="${PROGRAM_VERSION} 0"
fi
read ver count <<<"${reg}"
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
count=0
fi
echo ${count}
}
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
fi
else
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
fi
if [ -z "${PASSWORD}" ]; then
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
fi
if [ -r "${RESOURCE_FILE}" ]; then
source "${RESOURCE_FILE}"
else
exitMsg "resource file '${RESOURCE_FILE}' not found"
fi
if [ -r "${BUILD_REGISTER}" ]; then
BUILD_TAG=$(<"${BUILD_REGISTER}")
else
exitMsg "build register file '${BUILD_REGISTER}' not found"
fi
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/files"
#echo "URL: ${url}"
#echo $(curl --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
declare -a files=(
$(curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
)
for name in ${files[@]}; do
filename=${name:1:${#name}-2}
name_terminal=${filename##*_}
filever=${name_terminal%%.*}
if [ "${BUILD_TAG}" != "${PROGRAM_VERSION} ${filever}" ]; then
msgln "Deleting ${name}"
curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X DELETE ${GITEA_HOST}/api/packages/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${filename}
# else
# echo "most recent version"
fi
done
#curl --user "${GITEA_USER}:${PASSWORD}" -X GET https://git.portale-stac.it/api/v1/packages/go-pkg/generic/ecli/1.7.0/files
+236
View File
@@ -0,0 +1,236 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// commands.go
package main
import (
"fmt"
"io"
"os"
"strings"
"git.portale-stac.it/go-pkg/expr"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/utils"
)
type commandFunction func(opt *Options, ctx kern.ExprContext, args []string) (err error)
type command struct {
name string
description string
code commandFunction
}
func (cmd *command) exec(opt *Options, ctx kern.ExprContext, args []string) (err error) {
return cmd.code(opt, ctx, args)
}
type commandHandler struct {
cmdIndex []string
commands map[string]*command
}
func NewCommandHandler() *commandHandler {
return &commandHandler{
cmdIndex: make([]string, 0, 20),
commands: make(map[string]*command),
}
}
// func (h *commandHandler) setContext(ctx expr.ExprContext) {
// h.ctx = ctx
// }
func (h *commandHandler) add(name, description string, f commandFunction) {
h.cmdIndex = append(h.cmdIndex, name)
h.commands[name] = &command{name: name, description: description, code: f}
}
func (h *commandHandler) get(cmdLine string) (cmd *command, args []string) {
if len(cmdLine) > 0 {
tokens := strings.Split(cmdLine, " ")
name := tokens[0]
args = make([]string, 0, len(tokens)-1)
if cmd = h.commands[name]; cmd != nil && len(tokens) > 1 {
for _, tk := range tokens[1:] {
if tk != "" {
args = append(args, tk)
}
}
}
}
return
}
// ------
var cmdHandler *commandHandler
func (h *commandHandler) help() {
fmt.Fprintln(os.Stderr, `--- REPL commands:`)
for _, name := range h.cmdIndex {
cmd := h.commands[name]
fmt.Fprintf(os.Stderr, "%12s -- %s\n", cmd.name, cmd.description)
}
fmt.Fprint(os.Stderr, `
--- Command line options:
-b <builtin> Import builtin modules.
<builtin> can be a list of module names or a glob-pattern.
Use the special value 'all' or the pattern '*' to import all modules.
-B, --list-builtins List all builtin module names
-e <expression> Evaluate <expression> instead of standard-input
-i Force REPL operation when all -e occurences have been processed
-h, --help, help Show this help menu
-m, --modules List all builtin modules
--noout Disable printing of expression results
-p Print prefix form
-t Print tree form
-v, --version Show program version
`)
}
// --------
func cmdExit(opt *Options, ctx kern.ExprContext, args []string) (err error) {
return io.EOF
}
func cmdHelp(opt *Options, ctx kern.ExprContext, args []string) (err error) {
cmdHandler.help()
return
}
func cmdMultiLine(opt *Options, ctx kern.ExprContext, args []string) (err error) {
if opt.formOpt&kern.MultiLine == 0 {
opt.formOpt |= kern.MultiLine
} else {
opt.formOpt &= ^kern.MultiLine
}
return
}
func cmdTty(opt *Options, ctx kern.ExprContext, args []string) (err error) {
if opt.formOpt&kern.TTY == 0 {
opt.formOpt |= kern.TTY
} else {
opt.formOpt &= ^kern.TTY
}
return
}
func execFile(opt *Options, ctx kern.ExprContext, fileName string) (err error) {
var fh *os.File
if fh, err = os.Open(fileName); err == nil {
goBatch(opt, ctx, fh, false)
fh.Close()
}
return
}
func cmdSource(opt *Options, ctx kern.ExprContext, args []string) (err error) {
var target string
for _, arg := range args {
if len(arg) == 0 {
continue
}
// TODO migliorare questa parte: eventualmente valutare un'espressione
if target, err = checkStringLiteral(arg); err != nil {
break
}
if target, err = utils.ExpandPath(target); err != nil {
break
}
if isPattern(target) {
var fileNames []string
if fileNames, err = matchPathPattern(target); err == nil {
for _, fileName := range fileNames {
if err = execFile(opt, ctx, fileName); err != nil {
break
}
}
}
} else {
err = execFile(opt, ctx, target)
}
if err != nil {
break
}
}
return
}
func cmdModules(opt *Options, ctx kern.ExprContext, args []string) (err error) {
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
var check rune = ' '
if imported {
check = '*'
}
fmt.Printf("%c %20q: %s\n", check, name, description)
return true
})
return
}
func cmdBase(opt *Options, ctx kern.ExprContext, args []string) (err error) {
if len(args) == 0 {
fmt.Println(opt.base)
} else if args[0] == "2" {
opt.baseVerb = "0b%b"
opt.base = 2
} else if args[0] == "8" {
opt.baseVerb = "0o%o"
opt.base = 8
} else if args[0] == "10" {
opt.baseVerb = "%d"
opt.base = 10
} else if args[0] == "16" {
opt.baseVerb = "0x%x"
opt.base = 16
} else {
err = fmt.Errorf("invalid number base %s", args[0])
}
return
}
func cmdOutput(opt *Options, ctx kern.ExprContext, args []string) (err error) {
var outputArg string
if len(args) == 0 {
outputArg = "status"
} else {
outputArg = strings.ToLower(args[0])
}
switch outputArg {
case "on":
opt.output = true
case "off":
opt.output = false
case "status":
if opt.output {
fmt.Println("on")
} else {
fmt.Println("off")
}
default:
err = fmt.Errorf("output: unknown option %q", outputArg)
}
return
}
//------------------
func setupCommands() {
cmdHandler = NewCommandHandler()
cmdHandler.add("base", "Set the integer output base: 2, 8, 10, or 16", cmdBase)
cmdHandler.add("exit", "Exit the program", cmdExit)
cmdHandler.add("help", "Show command list", cmdHelp)
cmdHandler.add("ml", "Enable/Disable multi-line output", cmdMultiLine)
cmdHandler.add("mods", "List builtin modules", cmdModules)
cmdHandler.add("output", "Enable/Disable printing expression results. Options 'on', 'off', 'status'", cmdOutput)
cmdHandler.add("source", "Load a file as input", cmdSource)
cmdHandler.add("tty", "Enable/Disable ansi output", cmdTty)
}
+339
View File
@@ -0,0 +1,339 @@
= Ecli
Expression Calculator Interactive Tool
:authors: Celestino Amoroso
:email: celestino.amoroso@gmail.com
:docinfo: shared
:encoding: utf-8
:toc: right
:toclevels: 4
:icons: font
:icon-set: fi
:numbered:
:data-uri:
:docinfo1:
:sectlinks:
:sectanchors:
:source-highlighter: rouge
:rouge-style: manni
:stylesdir: /home/share/s3-howto/styles
:stylesheet: adoc-colony.css
// Workaround to manage double-column in back-tick quotes
:2c: ::
// Workaround to manage double-plus in back-tick quotes
:plusplus: ++
// Workaround to manage asterisk in back-tick quotes
:star: *
#Generated by Copilot#
== Overview
`ecli` (Expression Calculator Interactive Tool) is an interactive REPL (Read-Eval-Print Loop) application for evaluating expressions using the Expr package. It provides a powerful command-line interface for expression evaluation with support for multiple builtin modules, file operations, and interactive scripting.
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
- Command history survival across sessions
- Batch expression evaluation from scripts
- Data processing and transformation
- Mathematical computations with fractions and complex operators
== Getting Started
=== Installation
To build and install `ecli`:
[source,bash]
----
cd cmd/ecli
./build.bash
----
The compiled binary will be available as `ecli` in the current directory.
=== Basic Usage
Start the interactive REPL:
[source,bash]
----
./ecli
----
You'll see the prompt `>>>` where you can enter expressions to evaluate.
=== Command Line Options
[cols="1,4", options="header"]
|===
| Option | Description
| `-e <expression>` | Evaluate an expression directly without entering REPL mode
| `-i` | Force REPL operation after processing all `-e` options
| `-b <builtin>` | Import builtin modules (comma-separated list, glob patterns, or 'all')
| `-B, --list-builtins` | List all available builtin module names
| `-m, --modules` | List all builtin modules
| `-p` | Print expressions in prefix form
| `-t` | Print expressions in tree form
| `--noout` | Disable printing of expression results
| `-h, --help` | Show help message
| `-v, --version` | Show program version
|===
== Interactive Commands
Within the REPL, you can use the following commands:
[cols="2,5", options="header"]
|===
| Command | Description
| `base [2\|8\|10\|16]` | Set the integer output base. Use without arguments to show current base
| `exit` | Exit the REPL
| `help` | Display available commands and command-line options
| `ml` | Enable/Disable multi-line input mode for complex expressions
| `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
=== Expression Evaluation
`ecli` supports the full expression language provided by the Expr package, including:
- **Arithmetic Operations**: Addition, subtraction, multiplication, division, modulo
- **Bitwise Operations**: AND, OR, XOR, NOT, shift operations
- **Boolean Logic**: AND, OR, NOT operations
- **Relational Operators**: Comparison and equality operators
- **String Operations**: Concatenation and string manipulation
- **Iterators**: Range, list, and custom iterators
- **Functions**: Builtin and user-defined functions
- **Collections**: Lists, dictionaries, and linked lists
- **Fractions**: Support for fractional arithmetic
=== Builtin Modules
`ecli` provides access to various builtin modules through the `-b` option:
[cols="1,4", options="header"]
|===
| Module | Functionality
| `base` | Core expression evaluation functions
| `fmt` | String formatting and output functions
| `string` | String manipulation functions
| `math-arith` | Mathematical and arithmetic operations
| `iterator` | Iterator-related functions
| `os-file` | File I/O operations
| `import` | Module import functionality
|===
Use `-B` or `--list-builtins` to see all available modules:
[source,bash]
----
./ecli --list-builtins
----
== Examples
=== Basic Arithmetic
[source,bash]
----
>>> 2 + 3 * 4
14
>>> (2 + 3) * 4
20
----
=== String Operations
[source,bash]
----
>>> "hello" + " " + "world"
hello world
----
=== Using Iterators
[source,bash]
----
>>> it = [1, 2, 3, 4, 5] map $_ * 2
$($([#5]))
>>> $$(it)
[<
2,
4,
6,
8,
10
>]
----
=== Evaluating from Command Line
[source,bash]
----
./ecli -e "2 + 2"
4
----
=== Loading Builtin Modules
[source,bash]
----
./ecli -b "math-arith,string"
----
=== Loading Expressions from Files
Inside the REPL:
[source]
----
>>> source "expressions.expr"
----
Or from command line:
[source,bash]
----
./ecli -e "@expressions.expr"
----
== Configuration
=== Resource Files
`ecli` supports startup resource files:
- `.ecli.rc` - Main configuration file
- `.ecli.rc.d/` - Directory for modular configuration files
These files are automatically loaded at startup if they exist in the current directory or home directory.
== Building from Source
=== Prerequisites
- Go 1.18 or later
- Make or bash shell
=== Build Steps
[source,bash]
----
cd cmd/ecli
./build.bash
----
=== Build Artifacts
The build process generates:
- `ecli` - The main executable
- `version.txt` - Version information
- Platform-specific binaries (e.g., `ecli_v1.17.0_linux_amd64`, `ecli_v1.17.0_darwin_arm64`)
== Advanced Usage
=== Multi-line Input
For complex expressions, split operations across multiple lines terminating with a backslash (`\`):
[source]
----
>>> ml
>>> result = [1, 2, 3, 4, 5] \
... filter $_ > 2 \
... map $_ * 2
>>> $$(result)
[<6, 8, 10>
----
=== Script Execution
Create a file `calculations.expr`:
[source]
----
x = 10
y = 20
result = x + y * 2
----
Execute it:
[source,bash]
----
./ecli -e '@calculations.expr' -e 'result'
50
----
=== Other Iterator Examples
[source]
----
>>> data = [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]
// Extract names using map iterator
>>> $$(data map ($_).name)
[<
"alice",
"bob"
>]
// Filter ages greater than 28
>>> $$(data filter ($_).age > 28 map ($_).name)
[<
"alice"
>]
----
== Troubleshooting
=== Expression Parsing Errors
If you encounter parsing errors, check:
- Bracket matching and quotation marks
- Operator precedence
- Variable and function names
=== Module Loading Issues
Verify available modules:
[source,bash]
----
./ecli --list-builtins
----
=== File Not Found Errors
Ensure file paths are:
- Properly quoted in expressions
- Relative to the current working directory or absolute paths
- Readable by the current user
== Related Documentation
- link:../../../README.adoc[Expr Package Documentation]
- Expr Expression Language Syntax
- Builtin Modules Reference
== Version History
For version information and changes, see the link:version.txt[version file].
== License
Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). All rights reserved.
+15
View File
@@ -0,0 +1,15 @@
module ecli
go 1.24.0
require (
git.portale-stac.it/go-pkg/expr v0.33.0
git.portale-stac.it/go-pkg/utils v0.3.0
github.com/ergochat/readline v0.1.3
)
require (
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
)
+36
View File
@@ -0,0 +1,36 @@
git.portale-stac.it/go-pkg/expr v0.1.0 h1:7xGEuUhdh6RRFaRbRnLVqVJBmHJWHfqjDBm2K0fIW2s=
git.portale-stac.it/go-pkg/expr v0.1.0/go.mod h1:kUFEQkUMCJ1IiUKkL0P5/vznaAIzFI26Xf5P0rTXqR0=
git.portale-stac.it/go-pkg/expr v0.2.0 h1:AAaVsV0uaC4EikKU91VuubIpbIN7wuya7t4avyFgg+0=
git.portale-stac.it/go-pkg/expr v0.2.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
git.portale-stac.it/go-pkg/expr v0.17.0 h1:4ANGwJfwJO3AmnKka4Cf1AO9/ckGLMj8RIWeoDFKawQ=
git.portale-stac.it/go-pkg/expr v0.17.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
git.portale-stac.it/go-pkg/expr v0.32.0 h1:ikXqHjJslIGkD79G1/51xe+c25TFi2CslJ6nu8mOuJY=
git.portale-stac.it/go-pkg/expr v0.32.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
git.portale-stac.it/go-pkg/expr v0.33.0 h1:GJ7PPgA1689GSC/cUWGYm08jn7qMmkp0FMQf/As5sCw=
git.portale-stac.it/go-pkg/expr v0.33.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
git.portale-stac.it/go-pkg/utils v0.2.0 h1:2l4IVUhElzjaIUJlahPG2DZTGb9x7OXuFTO4z1K6LmY=
git.portale-stac.it/go-pkg/utils v0.2.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
git.portale-stac.it/go-pkg/utils v0.3.0 h1:kCJ3+XcekV7in/SieJjiswdtJKMBS0RTJMlG2fW5mK0=
git.portale-stac.it/go-pkg/utils v0.3.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
github.com/ergochat/readline v0.1.0 h1:KEIiAnyH9qGZB4K8oq5mgDcExlEKwmZDcyyocgJiABc=
github.com/ergochat/readline v0.1.0/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
github.com/ergochat/readline v0.1.1 h1:C8Uuo3ybB23GWOt0uxmHbGzKM9owmtXary6Clrj84s0=
github.com/ergochat/readline v0.1.1/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo=
github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
//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
package main
import (
"fmt"
"git.portale-stac.it/go-pkg/expr"
)
func printGraph() {
r := expr.NewExprReticle(ast)
fmt.Println(r.String())
}
+418
View File
@@ -0,0 +1,418 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// main.go
package main
import (
"bufio"
"fmt"
"io"
"os"
"strings"
"git.portale-stac.it/go-pkg/expr"
"git.portale-stac.it/go-pkg/expr/kern"
"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"
// https://pkg.go.dev/github.com/ergochat/readline#section-readme
"github.com/ergochat/readline"
)
const (
intro = PROGNAME + ` -- Expressions calculator ` + VERSION + `
Based on the Expr package ` + EXPR_VERSION + ` (` + EXPR_DATE + `)
Type help to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
`
mainPrompt = ">>> "
contPrompt = "... "
historyFile = "~/.expr_history"
)
// ------
func errOptValueRequired(opt string) error {
return fmt.Errorf("option %q requires a value", opt)
}
func about() string {
return PROGNAME + " -- " + VERSION + "; Expr package " + EXPR_VERSION
}
func importBuiltins(ctx kern.ExprContext, opt *Options) (err error) {
for _, spec := range opt.builtin {
if moduleSpec, ok := spec.(string); ok {
if moduleSpec == "all" {
moduleSpec = "*"
}
_, err = expr.ImportInContextByGlobPattern(ctx, moduleSpec)
} else if moduleSpec, ok := spec.([]string); ok {
notFoundList := make([]string, 0)
for _, name := range moduleSpec {
if !expr.ImportInContext(ctx, name) {
notFoundList = append(notFoundList, name)
}
}
if len(notFoundList) > 0 {
err = fmt.Errorf("not found modules: %s", strings.Join(notFoundList, ","))
}
}
}
return
}
func initReadlineConfig(cfg *readline.Config) {
if histfile, err := utils.ExpandPath(historyFile); err == nil {
cfg.HistoryFile = histfile
}
cfg.Undo = true
cfg.DisableAutoSaveHistory = true
}
func goInteractiveReadline(opt *Options, ctx kern.ExprContext, r io.Reader) {
var sb strings.Builder
var cfg readline.Config
initReadlineConfig(&cfg)
rl, err := readline.NewFromConfig(&cfg)
if err != nil {
goInteractive(opt, ctx, r)
return
}
defer rl.Close()
fmt.Print(intro)
rl.SetPrompt(mainPrompt)
for line, err := rl.ReadLine(); err == nil; line, err = rl.ReadLine() {
if continuation(&sb, line) {
rl.SetPrompt(contPrompt)
continue
}
rl.SetPrompt(mainPrompt)
sb.WriteString(line)
source := strings.TrimSpace(sb.String())
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
rl.SaveToHistory(source)
if err = cmd.exec(opt, ctx, args); err != nil {
if err == io.EOF {
err = nil
break
} else {
fmt.Fprintln(os.Stderr, "Eval Error:", err)
break
}
}
} else {
rl.SaveToHistory(source)
r := strings.NewReader(source)
compute(opt, ctx, r, true)
}
}
sb.Reset()
}
fmt.Println()
}
func goInteractive(opt *Options, ctx kern.ExprContext, r io.Reader) {
var sb strings.Builder
fmt.Print(intro)
fmt.Print(mainPrompt)
reader := bufio.NewReaderSize(r, 1024)
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
if continuation(&sb, line) {
continue
}
sb.WriteString(line)
source := strings.TrimSpace(sb.String())
// fmt.Printf("source=%q\n", source)
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
if err = cmd.exec(opt, ctx, args); err != nil {
break
}
} else {
r := strings.NewReader(source)
compute(opt, ctx, r, true)
}
}
sb.Reset()
fmt.Print(mainPrompt)
}
fmt.Println()
}
func continuation(sb *strings.Builder, line string) (cont bool) {
line = strings.TrimSpace(line)
if strings.HasSuffix(line, "\\") {
sb.WriteString(line[0 : len(line)-1])
cont = true
} else if strings.HasSuffix(line, ";") {
sb.WriteString(line)
cont = true
} else if len(line) > 0 {
if scan.StringEndsWithOperator(line) {
sb.WriteString(line)
cont = true
} else {
fullInput := sb.String() + line
if strings.Count(fullInput, "(") > strings.Count(fullInput, ")") ||
strings.Count(fullInput, "[") > strings.Count(fullInput, "]") ||
strings.Count(fullInput, "{") > strings.Count(fullInput, "}") {
sb.WriteString(line)
cont = true
}
}
}
return
}
func goBatch(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
var sb strings.Builder
reader := bufio.NewReaderSize(r, 1024)
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
if continuation(&sb, line) {
continue
}
sb.WriteString(line)
source := strings.TrimSpace(sb.String())
// fmt.Printf("source=%q\n", source)
if source != "" && !strings.HasPrefix(source, "//") {
if cmd, args := cmdHandler.get(source); cmd != nil {
if err = cmd.exec(opt, ctx, args); err != nil {
fmt.Fprintln(os.Stderr, "Eval Error:", err)
break
}
} else {
r := strings.NewReader(source)
compute(opt, ctx, r, outputEnabled)
}
}
sb.Reset()
}
}
func compute(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
scanner := scan.NewScanner(r, scan.DefaultTranslations())
parser := expr.NewParser()
if ast, err := parser.Parse(scanner); err == nil {
if opt.printPrefix {
fmt.Println(ast)
}
if opt.printTree {
printGraph()
}
if result, err := ast.Eval(ctx); err == nil {
if outputEnabled && opt.output {
printResult(opt, result)
}
} else {
fmt.Fprintln(os.Stderr, "Eval Error:", err)
}
} else {
fmt.Fprintln(os.Stderr, "Parse Error:", err)
}
}
func printResult(opt *Options, result any) {
if f, ok := result.(kern.Formatter); ok {
fmt.Println(f.ToString(opt.formOpt))
} else if types.IsInteger(result) {
fmt.Printf(opt.baseVerb, result)
fmt.Println()
} else if str.IsString(result) {
fmt.Printf("\"%s\"\n", result)
} else {
fmt.Println(result)
}
}
func isReaderTerminal(r io.Reader) bool {
if fh, ok := r.(*os.File); ok {
return utils.StreamIsTerminal(fh)
}
return false
}
func registerLocalFunctions(ctx kern.ExprContext) {
const (
devParamProp = "prop"
devParamDigits = "digits"
)
aboutFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = about()
return
}
ctrlListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
vars := ctx.EnumVars(func(name string) bool {
return len(name) > 0 && name[0] == '_'
})
result = array.ArrayFromStrings(vars)
return
}
ctrlFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
varName, _ := args[devParamProp].(string)
if len(args) == 1 {
result = expr.GlobalCtrlGet(ctx, varName)
} else {
result = expr.GlobalCtrlSet(ctx, varName, args[kern.ParamValue])
}
return
}
envSetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName, value string
var ok bool
if varName, ok = args[kern.ParamName].(string); !ok {
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
return
}
if value, ok = args[kern.ParamValue].(string); !ok {
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamValue])
return
}
if err = os.Setenv(varName, value); err == nil {
result = value
}
return
}
envGetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName string
var ok bool
if varName, ok = args[kern.ParamName].(string); !ok {
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
return
}
if result, ok = os.LookupEnv(varName); !ok {
err = fmt.Errorf("environment variable %q does not exist", varName)
}
return
}
envListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
env := os.Environ()
vars := make([]string, 0, len(env))
for _, e := range env {
name, _, _ := strings.Cut(e, "=")
vars = append(vars, name)
}
result = array.ArrayFromStrings(vars)
return
}
binFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var value, digits int64
var ok bool
var sb strings.Builder
if value, ok = args[kern.ParamValue].(int64); !ok {
err = kern.ErrExpectedGot(name, kern.TypeInt, args[kern.ParamValue])
return
}
if digits, ok = args[devParamDigits].(int64); !ok {
err = kern.ErrExpectedGot(name, kern.TypeInt, args[devParamDigits])
return
}
if digits != 64 && digits != 32 && digits != 16 && digits != 8 {
err = fmt.Errorf("%s param allows 8, 16, 32, or 64 values only", devParamDigits)
return
}
mask := uint64(0)
for i := 0; i < int(digits); i++ {
mask |= (1 << i)
}
maskedValue := uint64(value) & mask
// if maskedValue != uint64(value) {
// err = fmt.Errorf("%s param (%d) is not compatible with the value (%d) of %s param", expr.ParamValue, value, digits, devParamDigits)
// return
// }
for i := int(digits) - 1; i >= 0; i-- {
if maskedValue&(1<<i) == 0 {
sb.WriteByte('0')
} else {
sb.WriteByte('1')
}
}
result = sb.String()
return
}
ctx.RegisterFunc("about", kern.NewGolangFunctor(aboutFunc), kern.TypeString, []kern.ExprFuncParam{})
ctx.RegisterFunc("ctrlList", kern.NewGolangFunctor(ctrlListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
ctx.RegisterFunc("ctrl", kern.NewGolangFunctor(ctrlFunc), kern.TypeAny, []kern.ExprFuncParam{
kern.NewFuncParam(devParamProp),
kern.NewFuncParamFlag(kern.ParamValue, kern.PfOptional),
})
ctx.RegisterFunc("envSet", kern.NewGolangFunctor(envSetFunc), kern.TypeString, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamName),
kern.NewFuncParam(kern.ParamValue),
})
ctx.RegisterFunc("envGet", kern.NewGolangFunctor(envGetFunc), kern.TypeString, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamName),
})
ctx.RegisterFunc("envList", kern.NewGolangFunctor(envListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
ctx.RegisterFunc("bin", kern.NewGolangFunctor(binFunc), kern.TypeInt, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamValue),
kern.NewFuncParamFlagDef(devParamDigits, kern.PfOptional|kern.PfDefault, int64(8)),
})
}
func main() {
setupCommands()
opt := NewOptions()
opt.loadRc()
if err := opt.parseArgs(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
ctx := expr.NewSimpleStore()
if err := importBuiltins(ctx, opt); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
registerLocalFunctions(ctx)
if len(opt.expressions) == opt.rcCount || opt.forceInteractive {
opt.expressions = append(opt.expressions, os.Stdin)
}
for _, input := range opt.expressions {
if isReaderTerminal(input) {
goInteractiveReadline(opt, ctx, input)
} else {
_, enableOutput := input.(*strings.Reader)
goBatch(opt, ctx, input, enableOutput)
if f, ok := input.(*os.File); ok {
f.Close()
}
}
}
// TODO: why did I added these lines?
// if opt.output {
// printResult(opt, ctx.GetLast())
// }
}
+49
View File
@@ -0,0 +1,49 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// match.go
package main
import (
"os"
"path"
"path/filepath"
"strings"
)
func matchFilePattern(dirName string, pattern string, join bool) (fileList []string, err error) {
var entries []os.DirEntry
if entries, err = os.ReadDir(dirName); err == nil {
for _, entry := range entries {
if entry.Type().IsRegular() {
var match bool
if match, err =filepath.Match(pattern, entry.Name()); err != nil {
fileList = nil
break
}
if match {
if fileList == nil {
fileList = make([]string, 0, 1)
}
if join {
fileList = append(fileList, path.Join(dirName, entry.Name()))
} else {
fileList = append(fileList, entry.Name())
}
}
}
}
}
return
}
func matchPathPattern(pathPattern string) (fileList []string, err error) {
dirName := path.Dir(pathPattern)
pattern := path.Base(pathPattern)
return matchFilePattern(dirName, pattern, true)
}
func isPattern(name string) bool{
return strings.ContainsAny(name, "*?[]")
}
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// main.go
package main
import (
"path"
"slices"
"testing"
)
func TestIsPattern(t *testing.T) {
target := "non-pattern"
if isPattern(target) {
t.Errorf("%q recognized as a pattern", target)
}
target = "pattern/*.expr"
if !isPattern(target) {
t.Errorf("%q not recognized as a pattern", target)
}
}
func TestMatchFilePattern(t *testing.T) {
target := "./go.*sum"
dirName := path.Dir(target)
pattern := path.Base(target)
if matchedFiles, err := matchFilePattern(dirName, pattern, true); err == nil {
if slices.Compare(matchedFiles, []string{"go.sum", "go.work.sum"}) != 0 {
t.Errorf("Matched file list is not correct: %v", matchedFiles)
}
} else {
t.Errorf("Got error: %v", err)
}
}
+140
View File
@@ -0,0 +1,140 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// options.go
package main
import (
"fmt"
"io"
"os"
"strings"
"git.portale-stac.it/go-pkg/expr"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/utils"
)
type Options struct {
printTree bool
printPrefix bool
forceInteractive bool
builtin []any
expressions []io.Reader
formOpt kern.FmtOpt
baseVerb string
base int
output bool
rcCount int
}
func NewOptions() *Options {
return &Options{
expressions: make([]io.Reader, 0),
builtin: make([]any, 0),
formOpt: kern.Base10,
baseVerb: "%d",
base: 10,
output: true,
rcCount: 0,
}
}
func (opt *Options) loadRc() {
var rcPath string
var fh *os.File
var err error
rcList := []string{
".ecli.rc",
"~/.ecli.rc",
"~/.config/expr/ecli.rc",
"~/.dev-expr.rc", // OBSOLETE, to be removed in future releases
}
for _, rcFile := range rcList {
if rcPath, err = utils.ExpandPath(rcFile); err != nil {
return
}
if fh, err = os.Open(rcPath); err == nil {
opt.expressions = append(opt.expressions, fh) // rc should be the first source to be read
opt.rcCount++
break
}
}
}
func listBuiltins() {
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
if imported {
name = "*" + name
}
fmt.Printf("%20q: %s\n", name, description)
return true
})
}
func (opt *Options) parseArgs() (err error) {
for i := 1; i < len(os.Args) && err == nil; i++ {
arg := os.Args[i]
switch arg {
case "-i":
opt.forceInteractive = true
case "-t":
opt.printTree = true
case "-p":
opt.printPrefix = true
case "-e":
if i+1 < len(os.Args) {
i++
spec := os.Args[i]
if strings.HasPrefix(spec, "@") {
var f *os.File
if f, err = os.Open(spec[1:]); err == nil {
opt.expressions = append(opt.expressions, f)
} else {
return
}
} else {
if len(spec) > 0 && spec[len(spec)-1] != '\n' {
spec += "\n"
}
opt.expressions = append(opt.expressions, strings.NewReader(spec))
}
} else {
err = errOptValueRequired(arg)
}
case "-b":
if i+1 < len(os.Args) {
i++
specs := strings.Split(os.Args[i], ",")
if len(specs) == 1 {
opt.builtin = append(opt.builtin, specs[0])
} else {
opt.builtin = append(opt.builtin, specs)
}
} else {
err = errOptValueRequired(arg)
}
case "-B", "--list-builtins":
listBuiltins()
os.Exit(0)
case "-m", "--modules":
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
fmt.Printf("%20q: %s\n", name, description)
return true
})
os.Exit(0)
case "--noout":
opt.output = false
case "-h", "--help", "help":
cmdHandler.help()
os.Exit(0)
case "-v", "--version", "version", "about":
fmt.Println(about())
os.Exit(0)
default:
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
}
}
return
}
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
# All rights reserved.
RESOURCE_FILE=".build.rc"
BUILD_REGISTER=".build_register"
PASSWORD=
GITEA_USER="camoroso"
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
GITEA_OWNER="go-pkg"
GITEA_HOST="https://git.portale-stac.it"
GITEA_BASE_PATH="api/packages"
GITEA_PKG_TYPE="generic"
function exitMsg() {
echo >&2 "${1}"
exit 1
}
function readBuildCount() {
local reg ver count
if [ -r "${BUILD_REGISTER}" ]; then
reg=$(<"${BUILD_REGISTER}")
else
reg="${PROGRAM_VERSION} 0"
fi
read ver count <<<"${reg}"
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
count=0
fi
echo ${count}
}
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
fi
else
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
fi
if [ -z "${PASSWORD}" ]; then
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
fi
if ! ./build.bash; then
exitMsg "Build program failed"
fi
if ! source "${RESOURCE_FILE}"; then
exitMsg "Loading resource file failed"
fi
if ! exeList=$(echo 2>/dev/null ${PROGRAM_NAME}_v${PROGRAM_VERSION}_*); then
exitMsg "No executable found"
fi
buildCount=$(readBuildCount)
fileCount=0
for exe in ${exeList}; do
if [ "${exe/tar.gz/}" != "${exe}" ]; then
continue
fi
((fileCount++))
dir="${exe}_${buildCount}"
dist="${dir}.tar.gz"
rm -f "${dist}"
printf "%2d: %-30s --> %s\n" "${fileCount}" "${exe}" "${dist}"
mkdir "${dir}"
cp "${exe}" "${dir}/${PROGRAM_NAME}"
tar czf "${dist}" "${dir}"
rm -fR "${dir}"
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${dist}"
# echo "${url}"
curl --user "${USER}:${PASSWORD}" --upload-file "${dist}" "${url}"
rm -f "${dist}"
done
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// util-string.go
package main
import (
"fmt"
)
func checkStringLiteral(literal string) (value string, err error) {
length := len(literal)
if length >= 2 {
if (literal[0] == '"' && literal[length-1] == '"') || literal[0] == '\'' && literal[length-1] == '\'' {
value = literal[1 : length-1]
} else {
err = fmt.Errorf("unquoted or partially quoted string literal: `%s`", literal)
}
} else {
err = fmt.Errorf("invalid string literal: `%s`", literal)
}
return
}
+1
View File
@@ -0,0 +1 @@
1.17.0
+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
+331 -150
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)
} }
-2
View File
@@ -1,4 +1,2 @@
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+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)
+9 -7
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
@@ -89,16 +90,17 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
if dir, err = util.ExpandPath(dir); err != nil { if dir, err = util.ExpandPath(dir); err != nil {
continue continue
} }
if fullPath := path.Join(dir, filename); isFile(fullPath) { fullPath := path.Join(dir, filename)
if isFile(fullPath) {
filePath = fullPath filePath = fullPath
break break
} }
subdir := strings.TrimSuffix(filename, suffix) // subdir := strings.TrimSuffix(filename, suffix)
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) { // if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
filePath = fullPath // filePath = fullPath
break // break
} // }
} }
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)
} }
+112 -109
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 break
} }
}
// 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) 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
}
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
}
} 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 = true firstToken = tk.IsOneOfA(sym.SymColon, sym.SymInterval)
} }
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)
+2 -2
View File
@@ -33,9 +33,9 @@ func pluginExists(name string) (exists bool) {
func makePluginName(name string) (decorated string) { func makePluginName(name string) (decorated string) {
var template string var template string
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") { if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
template = "expr-%s-plugin%s" template = "expr-%s%s"
} else { } else {
template = "expr-%s-plugin%s.debug" template = "expr-%s%s.debug"
} }
decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION) decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION)
return return
+150 -122
View File
@@ -12,7 +12,7 @@ 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"
) )
@@ -23,10 +23,12 @@ type Scanner struct {
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,7 +483,11 @@ 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 == '.' {
// do nothing, this is an interval operator
scanner.setUnreadChar('.')
} else {
symbol = sym.SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = scanner.readChar() ch, err = scanner.readChar()
if ch >= '0' && ch <= '9' { if ch >= '0' && ch <= '9' {
@@ -473,9 +496,11 @@ func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
} }
} }
} }
}
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)
} }

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