Compare commits

..

30 Commits

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

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