Compare commits

..

31 Commits

Author SHA1 Message Date
camoroso 99c1adc434 copyright header updated to 2026 2026-05-06 09:27:42 +02:00
camoroso 5585b496fb iter-iter: changed item operation from function and args to a list of expressions 2026-05-06 04:04:08 +02:00
camoroso acd4f8487d new iter-iter iterator and kern.func-info module 2026-05-05 20:38:30 +02:00
camoroso 7f34ccf955 moved scanner sources to package 'scan' 2026-05-03 14:19:17 +02:00
camoroso f63ff5953e graph.go: conditioned compilation by 'graph' tag 2026-05-03 07:14:30 +02:00
camoroso b9d37a5b4c kern/compare.go: added copyright comment 2026-05-03 07:13:39 +02:00
camoroso 23b8eec74a builtin-base: removed useless function unset(). See UNSET operator 2026-05-03 06:46:51 +02:00
camoroso bb6b6d17ec operator-map.go: return nil on error 2026-05-03 06:30:56 +02:00
camoroso 53acacbadf kern/common-errors.go: little changes to ErrExpectedGot() and ErrInvalidParameterValue() 2026-05-03 06:30:00 +02:00
camoroso 2ebc52891c Iterator operator: automatic temporary variables _index and _count changed with '__' and '_#' respectively. Note that, sinc '#' is not an identifier allowed char, '_#' requires himBHsnotation: -cover 2026-05-02 15:06:12 +02:00
camoroso 3b2ef7927b new operator 'groupby' 2026-05-02 14:53:19 +02:00
camoroso d5ced343c4 kern/compare.go: new function Equal(a,b) that returns true if a and b have the same value 2026-05-02 14:45:05 +02:00
camoroso 3ac8cab275 enhanced ending operator detection 2026-05-02 09:54:24 +02:00
camoroso 6c5e9db34b int-itrator: new iterator over integer ranges 2026-05-01 17:23:06 +02:00
camoroso 78871641d0 t_common_test.go: Error messages also contains the name of the section introduced by special symbol >>> 2026-05-01 17:17:40 +02:00
camoroso dacbec677a iterator interface chenged index and count members from int to tint 64 2026-05-01 17:15:18 +02:00
camoroso 75ed88915d ast.go, pasrse.go: fixed the recover routine 2026-05-01 17:03:44 +02:00
camoroso f2d1f23774 ast.go: added recover from panic in ast.Eval() 2026-05-01 07:25:05 +02:00
camoroso edd90054d7 parser.go: added recover from panic in parser.Parse() 2026-05-01 07:24:41 +02:00
camoroso 610e2df5f5 operator-dot: added support to access dict value by exprssion; example: D.key, D."key", D.expr 2026-04-30 07:06:20 +02:00
camoroso 32c0b45255 kern/dict-type.go: added GetItem() 2026-04-30 07:04:03 +02:00
camoroso 75a3f220df parser: token '()' returns error 2026-04-30 07:03:28 +02:00
camoroso 116b54836f token.go: little change to ErrorExpectedGotStringWithPrefix() 2026-04-30 07:01:02 +02:00
camoroso 8787973de0 util files moved form kern to util package 2026-04-29 11:03:36 +02:00
camoroso 4d910dd069 moved a subset of source file to the kern package 2026-04-27 19:43:37 +02:00
camoroso f100adead3 increased test coverage and splitted some utility functions by OS 2026-04-26 06:16:43 +02:00
camoroso 7d2cf1e687 new operator 'join' 2026-04-25 07:00:30 +02:00
camoroso 49728307f3 t_operator_test.go: added a test for the digest operator 2026-04-25 06:33:35 +02:00
camoroso 20dc502438 new operator 'digest' 2026-04-25 06:25:39 +02:00
camoroso ce7bfc5e3f operator-map/filter.go: added deletion of temporary variables '_index' and '_count' 2026-04-25 06:24:28 +02:00
camoroso 6e98bdd16b new operator 'filter' 2026-04-24 22:42:46 +02:00
143 changed files with 5470 additions and 3610 deletions
-130
View File
@@ -1,130 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// ast.go
package expr
import (
"strings"
)
//-------- ast
type ast struct {
forest []*term
root *term
}
func NewAst() *ast {
return &ast{}
}
func (expr *ast) TypeName() string {
return "Expression"
}
func (expr *ast) ToForest() {
if expr.root != nil {
if expr.forest == nil {
expr.forest = make([]*term, 0)
}
expr.forest = append(expr.forest, expr.root)
expr.root = nil
}
}
func (expr *ast) String() string {
var sb strings.Builder
if expr.root == nil {
sb.WriteString("(nil)")
} else {
expr.root.toString(&sb)
}
return sb.String()
}
func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens {
if _, err = expr.addToken(tk); err != nil {
break
}
}
return
}
// func (expr *ast) addToken(tk *Token) (err error) {
// _, err = expr.addToken2(tk)
// return
// }
func (expr *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil {
err = expr.addTerm(t)
} else {
err = tk.Errorf("unexpected token %q", tk.String())
}
return
}
func (expr *ast) addTerm(node *term) (err error) {
if expr.root == nil {
expr.root = node
} else {
expr.root, err = expr.insert(expr.root, node)
}
return
}
func (expr *ast) insert(tree, node *term) (root *term, err error) {
if tree.getPriority() < node.getPriority() {
root = tree
if tree.isComplete() {
var subRoot *term
last := tree.removeLastChild()
if subRoot, err = expr.insert(last, node); err == nil {
subRoot.setParent(tree)
}
} else {
node.setParent(tree)
}
} else if !node.isLeaf() {
root = node
tree.setParent(node)
} else {
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
}
return
}
func (expr *ast) Finish() {
if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
expr.root = expr.forest[len(expr.forest)-1]
expr.forest = expr.forest[0 : len(expr.forest)-1]
}
}
func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
expr.Finish()
if expr.root != nil {
// initDefaultVars(ctx)
if expr.forest != nil {
for _, root := range expr.forest {
if result, err = root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result)
} else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break
}
}
}
if err == nil {
if result, err = expr.root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result)
}
}
// } else {
// err = errors.New("empty expression")
}
return
}
+17 -13
View File
@@ -1,23 +1,27 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// function.go // function.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// ---- Linking with Expr functions // ---- Linking with Expr functions
type exprFunctor struct { type exprFunctor struct {
baseFunctor kern.BaseFunctor
params []ExprFuncParam params []kern.ExprFuncParam
expr Expr expr kern.Expr
defCtx ExprContext defCtx kern.ExprContext
} }
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) { func (functor *exprFunctor) GetParams() (params []kern.ExprFuncParam) {
return functor.params return functor.params
} }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor { func newExprFunctor(e kern.Expr, params []kern.ExprFuncParam, ctx kern.ExprContext) *exprFunctor {
var defCtx ExprContext var defCtx kern.ExprContext
if ctx != nil { if ctx != nil {
defCtx = ctx defCtx = ctx
} }
@@ -28,17 +32,17 @@ func (functor *exprFunctor) TypeName() string {
return "ExprFunctor" return "ExprFunctor"
} }
func (functor *exprFunctor) GetDefinitionContext() ExprContext { func (functor *exprFunctor) GetDefinitionContext() kern.ExprContext {
return functor.defCtx return functor.defCtx
} }
func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) { func (functor *exprFunctor) InvokeNamed(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var missing []string var missing []string
for _, p := range functor.params { for _, p := range functor.params {
if arg, exists := args[p.Name()]; exists { if arg, exists := args[p.Name()]; exists {
if funcArg, ok := arg.(Functor); ok { if funcArg, ok := arg.(kern.Functor); ok {
paramSpecs := funcArg.GetParams() paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs) ctx.RegisterFunc(p.Name(), funcArg, kern.TypeAny, paramSpecs)
} else { } else {
ctx.UnsafeSetVar(p.Name(), arg) ctx.UnsafeSetVar(p.Name(), arg)
} }
@@ -51,7 +55,7 @@ func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[s
} }
} }
if missing != nil { if missing != nil {
err = ErrMissingParams(name, missing) err = kern.ErrMissingParams(name, missing)
} else { } else {
result, err = functor.expr.Eval(ctx) result, err = functor.expr.Eval(ctx)
} }
-23
View File
@@ -1,23 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bind-go-function.go
package expr
// ---- Linking with Go functions
type golangFunctor struct {
baseFunctor
f FuncTemplate
}
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f}
}
func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+118 -115
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-base.go // builtin-base.go
@@ -9,81 +9,84 @@ import (
"math" "math"
"strconv" "strconv"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
const ( const (
ParamDenominator = "denominator" ParamDenominator = "denominator"
) )
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = args[ParamValue] == nil result = args[kern.ParamValue] == nil
return return
} }
func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsInteger(args[ParamValue]) result = kern.IsInteger(args[kern.ParamValue])
return return
} }
func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFloat(args[ParamValue]) result = kern.IsFloat(args[kern.ParamValue])
return return
} }
func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsBool(args[ParamValue]) result = kern.IsBool(args[kern.ParamValue])
return return
} }
func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsString(args[ParamValue]) result = kern.IsString(args[kern.ParamValue])
return return
} }
func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFract(args[ParamValue]) result = kern.IsFraction(args[kern.ParamValue])
return return
} }
func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsRational(args[ParamValue]) result = kern.IsRational(args[kern.ParamValue])
return return
} }
func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsList(args[ParamValue]) result = kern.IsList(args[kern.ParamValue])
return return
} }
func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsDict(args[ParamValue]) result = kern.IsDict(args[kern.ParamValue])
return return
} }
func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = (v != 0) result = (v != 0)
case *FractionType: case *kern.FractionType:
result = v.num != 0 result = v.N() != 0
case float64: case float64:
result = v != 0.0 result = v != 0.0
case bool: case bool:
result = v result = v
case string: case string:
result = len(v) > 0 result = len(v) > 0
case *ListType: case *kern.ListType:
result = len(*v) > 0 result = len(*v) > 0
case *DictType: case *kern.DictType:
result = len(*v) > 0 result = len(*v) > 0
default: default:
err = ErrCantConvert(name, v, "bool") err = kern.ErrCantConvert(name, v, "bool")
} }
return return
} }
func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = v result = v
case float64: case float64:
@@ -99,16 +102,16 @@ func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err
if i, err = strconv.Atoi(v); err == nil { if i, err = strconv.Atoi(v); err == nil {
result = int64(i) result = int64(i)
} }
case *FractionType: case *kern.FractionType:
result = int64(v.num / v.den) result = int64(v.N() / v.D())
default: default:
err = ErrCantConvert(name, v, "int") err = kern.ErrCantConvert(name, v, "int")
} }
return return
} }
func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = float64(v) result = float64(v)
case float64: case float64:
@@ -124,16 +127,16 @@ func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err
if f, err = strconv.ParseFloat(v, 64); err == nil { if f, err = strconv.ParseFloat(v, 64); err == nil {
result = f result = f
} }
case *FractionType: case *kern.FractionType:
result = v.toFloat() result = v.ToFloat()
default: default:
err = ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
} }
return return
} }
func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func stringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = strconv.FormatInt(v, 10) result = strconv.FormatInt(v, 10)
case float64: case float64:
@@ -146,169 +149,169 @@ func stringFunc(ctx ExprContext, name string, args map[string]any) (result any,
} }
case string: case string:
result = v result = v
case *FractionType: case *kern.FractionType:
result = v.ToString(0) result = v.ToString(0)
case Formatter: case kern.Formatter:
result = v.ToString(0) result = v.ToString(0)
case fmt.Stringer: case fmt.Stringer:
result = v.String() result = v.String()
default: default:
err = ErrCantConvert(name, v, "string") err = kern.ErrCantConvert(name, v, "string")
} }
return return
} }
func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func fractFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[ParamValue].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
var den int64 = 1 var den int64 = 1
var ok bool var ok bool
if den, ok = args[ParamDenominator].(int64); !ok { if den, ok = args[ParamDenominator].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[ParamDenominator]) err = kern.ErrExpectedGot(name, "integer", args[ParamDenominator])
} else if den == 0 { } else if den == 0 {
err = ErrFuncDivisionByZero(name) err = kern.ErrFuncDivisionByZero(name)
} }
if err == nil { if err == nil {
result = newFraction(v, den) result = kern.NewFraction(v, den)
} }
case float64: case float64:
result, err = float64ToFraction(v) result, err = kern.Float64ToFraction(v)
case bool: case bool:
if v { if v {
result = newFraction(1, 1) result = kern.NewFraction(1, 1)
} else { } else {
result = newFraction(0, 1) result = kern.NewFraction(0, 1)
} }
case string: case string:
result, err = makeGeneratingFraction(v) result, err = kern.MakeGeneratingFraction(v)
case *FractionType: case *kern.FractionType:
result = v result = v
default: default:
err = ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
} }
return return
} }
// func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) { // func iteratorFunc(ctx expr.ExprContext, name string, args []any) (result any, err error) {
// return // return
// } // }
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func evalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok { if source, ok := args[kern.ParamSource].(string); ok {
var expr Expr var ast kern.Expr
parser := NewParser() parser := NewParser()
if ctx == nil { if ctx == nil {
ctx = NewSimpleStore() ctx = NewSimpleStoreWithoutGlobalContext()
} }
r := strings.NewReader(source) r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations()) scanner := scan.NewScanner(r, scan.DefaultTranslations())
if expr, err = parser.Parse(scanner); err == nil { if ast, err = parser.Parse(scanner); err == nil {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, kern.ControlExportAll)
result, err = expr.Eval(ctx) result, err = ast.Eval(ctx)
} }
} else { } else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
return return
} }
func varFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func varFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName string var varName string
var ok bool var ok bool
if varName, ok = args[ParamName].(string); !ok { if varName, ok = args[kern.ParamName].(string); !ok {
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName]) return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
} }
if result, ok = args[ParamValue]; ok && result != nil { if result, ok = args[kern.ParamValue]; ok && result != nil {
ctx.GetParent().UnsafeSetVar(varName, result) ctx.GetParent().UnsafeSetVar(varName, result)
// } else { // } else {
// err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) // err = expr.ErrWrongParamType(name, expr.ParamSource, expr.TypeString, args[expr.ParamSource])
// } // }
} else if result, ok = ctx.GetVar(varName); !ok { } else if result, ok = ctx.GetVar(varName); !ok {
err = ErrUnknownVar(name, varName) err = kern.ErrUnknownVar(name, varName)
} }
return return
} }
func setFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName string var varName string
var ok bool var ok bool
if varName, ok = args[ParamName].(string); !ok { if varName, ok = args[kern.ParamName].(string); !ok {
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName]) return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
} }
if result, ok = args[ParamValue]; ok { if result, ok = args[kern.ParamValue]; ok {
ctx.GetParent().UnsafeSetVar(varName, result) ctx.GetParent().UnsafeSetVar(varName, result)
} else { } else {
err = ErrWrongParamType(name, ParamValue, TypeAny, args[ParamValue]) err = kern.ErrWrongParamType(name, kern.ParamValue, kern.TypeAny, args[kern.ParamValue])
} }
return return
} }
func unsetFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { // func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName string // var varName string
var ok bool // var ok bool
if varName, ok = args[ParamName].(string); !ok { // if varName, ok = args[kern.ParamName].(string); !ok {
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName]) // return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
} else { // } else {
ctx.GetParent().DeleteVar(varName) // ctx.GetParent().DeleteVar(varName)
result = nil // result = nil
} // }
return // return
} // }
//// import //// import
func ImportBuiltinsFuncs(ctx ExprContext) { func ImportBuiltinsFuncs(ctx kern.ExprContext) {
anyParams := []ExprFuncParam{ anyParams := []kern.ExprFuncParam{
NewFuncParam(ParamValue), kern.NewFuncParam(kern.ParamValue),
} }
ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isNil", kern.NewGolangFunctor(isNilFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isInt", kern.NewGolangFunctor(isIntFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isFloat", kern.NewGolangFunctor(isFloatFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isBool", kern.NewGolangFunctor(isBoolFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams) ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams) ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams) ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams) ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams)
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams) ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{ ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
NewFuncParam(ParamValue), kern.NewFuncParam(kern.ParamValue),
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)), kern.NewFuncParamFlagDef(ParamDenominator, kern.PfDefault, int64(1)),
}) })
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("eval", kern.NewGolangFunctor(evalFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
}) })
ctx.RegisterFunc("var", NewGolangFunctor(varFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("var", kern.NewGolangFunctor(varFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(ParamName), kern.NewFuncParam(kern.ParamName),
NewFuncParamFlagDef(ParamValue, PfDefault, nil), kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault, nil),
}) })
ctx.RegisterFunc("set", NewGolangFunctor(setFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("set", kern.NewGolangFunctor(setFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(ParamName), kern.NewFuncParam(kern.ParamName),
NewFuncParam(ParamValue), kern.NewFuncParam(kern.ParamValue),
}) })
ctx.RegisterFunc("unset", NewGolangFunctor(unsetFunc), TypeAny, []ExprFuncParam{ // ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(ParamName), // kern.NewFuncParam(kern.ParamName),
NewFuncParam(ParamValue), // kern.NewFuncParam(kern.ParamValue),
}) // })
} }
func init() { func init() {
+14 -12
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-fmt.go // builtin-fmt.go
@@ -8,11 +8,13 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func getStdout(ctx ExprContext) io.Writer { func getStdout(ctx kern.ExprContext) io.Writer {
var w io.Writer var w io.Writer
if wany, exists := ctx.GetVar(ControlStdout); exists && wany != nil { if wany, exists := ctx.GetVar(kern.ControlStdout); exists && wany != nil {
w, _ = wany.(io.Writer) w, _ = wany.(io.Writer)
} }
if w == nil { if w == nil {
@@ -21,9 +23,9 @@ func getStdout(ctx ExprContext) io.Writer {
return w return w
} }
func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func printFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0 var n int = 0
if v, exists := args[ParamItem]; exists && v != nil { if v, exists := args[kern.ParamItem]; exists && v != nil {
argv := v.([]any) argv := v.([]any)
n, err = fmt.Fprint(getStdout(ctx), argv...) n, err = fmt.Fprint(getStdout(ctx), argv...)
} }
@@ -31,9 +33,9 @@ func printFunc(ctx ExprContext, name string, args map[string]any) (result any, e
return return
} }
func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func printLnFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0 var n int = 0
if v, exists := args[ParamItem]; exists && v != nil { if v, exists := args[kern.ParamItem]; exists && v != nil {
argv := v.([]any) argv := v.([]any)
n, err = fmt.Fprintln(getStdout(ctx), argv...) n, err = fmt.Fprintln(getStdout(ctx), argv...)
} else { } else {
@@ -43,12 +45,12 @@ func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any,
return return
} }
func ImportFmtFuncs(ctx ExprContext) { func ImportFmtFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("print", kern.NewGolangFunctor(printFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParamFlag(ParamItem, PfRepeat), kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
}) })
ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("println", kern.NewGolangFunctor(printLnFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParamFlag(ParamItem, PfRepeat), kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
}) })
} }
+20 -17
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-import.go // builtin-import.go
@@ -7,32 +7,35 @@ package expr
import ( import (
"io" "io"
"os" "os"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
func importFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
return importGeneral(ctx, name, args) return importGeneral(ctx, name, args)
} }
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func importAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, kern.ControlExportAll)
return importGeneral(ctx, name, args) return importGeneral(ctx, name, args)
} }
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) { func importGeneral(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH) dirList := buildSearchDirList(ctx, "sources", ENV_EXPR_SOURCE_PATH)
if v, exists := args[ParamFilepath]; exists && v != nil { if v, exists := args[kern.ParamFilepath]; exists && v != nil {
argv := v.([]any) argv := v.([]any)
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv)) result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
} }
return return
} }
func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) { func doImport(ctx kern.ExprContext, name string, dirList []string, it kern.Iterator) (result any, err error) {
var v any var v any
var sourceFilepath string var sourceFilepath string
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if err = checkStringParamExpected(name, v, it.Index()); err != nil { if err = checkStringParamExpected(name, v, int(it.Index())); err != nil {
break break
} }
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil { if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
@@ -41,10 +44,10 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
var file *os.File var file *os.File
if file, err = os.Open(sourceFilepath); err == nil { if file, err = os.Open(sourceFilepath); err == nil {
defer file.Close() defer file.Close()
var expr *ast var expr *scan.Ast
scanner := NewScanner(file, DefaultTranslations()) scanner := scan.NewScanner(file, scan.DefaultTranslations())
parser := NewParser() parser := NewParser()
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil { if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymEos); err == nil {
result, err = expr.Eval(ctx) result, err = expr.Eval(ctx)
} }
if err != nil { if err != nil {
@@ -64,12 +67,12 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
return return
} }
func ImportImportFuncs(ctx ExprContext) { func ImportImportFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("import", kern.NewGolangFunctor(importFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat), kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
}) })
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("importAll", kern.NewGolangFunctor(importAllFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat), kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
}) })
} }
+21 -28
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-iterator.go // builtin-iterator.go
@@ -7,6 +7,8 @@ package expr
import ( import (
"fmt" "fmt"
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern"
) )
const ( const (
@@ -15,24 +17,24 @@ const (
iterVarStatus = "status" iterVarStatus = "status"
) )
func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Functor, err error) { func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
var ok bool var ok bool
if it, ok = args[ParamIterator].(Iterator); !ok { if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator])) err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
return return
} }
if args[iterParamOperator] != nil { if args[iterParamOperator] != nil {
if op, ok = args[iterParamOperator].(Functor); !ok || op == nil { if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator])) err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
return return
} }
} }
var vars *DictType var vars *kern.DictType
if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil { 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], TypeName(args[iterParamVars])) err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
return return
} }
@@ -48,10 +50,10 @@ func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Fu
return return
} }
func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var it Iterator var it kern.Iterator
var ok bool var ok bool
var op Functor var op kern.Functor
var v any var v any
// var usingDefaultOp = false // var usingDefaultOp = false
var params map[string]any var params map[string]any
@@ -62,25 +64,16 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
if it, op, err = parseRunArgs(localCtx, args); err != nil { if it, op, err = parseRunArgs(localCtx, args); err != nil {
return return
// } else if op == nil {
// op = NewGolangFunctor(printLnFunc)
// usingDefaultOp = true
} }
for item, err = it.Next(); err == nil; item, err = it.Next() { for item, err = it.Next(); err == nil; item, err = it.Next() {
// if usingDefaultOp {
// params = map[string]any{ParamItem: []any{item}}
// } else {
// params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
// }
if op != nil { if op != nil {
params = map[string]any{ParamIndex: it.Index(), ParamItem: item} params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil { if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break break
} else { } else {
var success bool var success bool
if success, ok = ToBool(v); !success || !ok { if success, ok = kern.ToBool(v); !success || !ok {
break break
} }
} }
@@ -99,11 +92,11 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
return return
} }
func ImportIterFuncs(ctx ExprContext) { func ImportIterFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("run", kern.NewGolangFunctor(runFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(ParamIterator), kern.NewFuncParam(kern.ParamIterator),
NewFuncParamFlag(iterParamOperator, PfOptional), kern.NewFuncParamFlag(iterParamOperator, kern.PfOptional),
NewFuncParamFlag(iterParamVars, PfOptional), kern.NewFuncParamFlag(iterParamVars, kern.PfOptional),
}) })
} }
+45 -43
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-math-arith.go // builtin-math-arith.go
@@ -7,67 +7,69 @@ package expr
import ( import (
"fmt" "fmt"
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
if !(IsNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ { if !(kern.IsNumber(paramValue) || kern.IsFraction(paramValue)) /*|| isList(paramValue)*/ {
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
funcName, paramPos+1, subPos+1, level, paramValue) funcName, paramPos+1, subPos+1, level, paramValue)
} }
return return
} }
func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
var sumAsFloat, sumAsFract bool var sumAsFloat, sumAsFract bool
var floatSum float64 = 0.0 var floatSum float64 = 0.0
var intSum int64 = 0 var intSum int64 = 0
var fractSum *FractionType var fractSum *kern.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*ListType); ok { if list, ok := v.(*kern.ListType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
if v, err = doAdd(ctx, name, subIter, count, level); err != nil { if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) { if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil { if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
return return
} }
} }
} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { } else if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
break break
} }
count++ count++
if !sumAsFloat { if !sumAsFloat {
if IsFloat(v) { if kern.IsFloat(v) {
sumAsFloat = true sumAsFloat = true
if sumAsFract { if sumAsFract {
floatSum = fractSum.toFloat() floatSum = fractSum.ToFloat()
} else { } else {
floatSum = float64(intSum) floatSum = float64(intSum)
} }
} else if !sumAsFract && isFraction(v) { } else if !sumAsFract && kern.IsFraction(v) {
fractSum = newFraction(intSum, 1) fractSum = kern.NewFraction(intSum, 1)
sumAsFract = true sumAsFract = true
} }
} }
if sumAsFloat { if sumAsFloat {
floatSum += numAsFloat(v) floatSum += kern.NumAsFloat(v)
} else if sumAsFract { } else if sumAsFract {
var item *FractionType var item *kern.FractionType
var ok bool var ok bool
if item, ok = v.(*FractionType); !ok { if item, ok = v.(*kern.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = newFraction(iv, 1) item = kern.NewFraction(iv, 1)
} }
fractSum = sumFract(fractSum, item) fractSum = kern.SumFract(fractSum, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intSum += iv intSum += iv
@@ -86,64 +88,64 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func addFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any) argv := args[kern.ParamValue].([]any)
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1) result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
var mulAsFloat, mulAsFract bool var mulAsFloat, mulAsFract bool
var floatProd float64 = 1.0 var floatProd float64 = 1.0
var intProd int64 = 1 var intProd int64 = 1
var fractProd *FractionType var fractProd *kern.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*ListType); ok { if list, ok := v.(*kern.ListType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
if v, err = doMul(ctx, name, subIter, count, level); err != nil { if v, err = doMul(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) { if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
if _, err = extIter.CallOperation(CleanName, nil); err != nil { if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
return return
} }
} }
} else { } else {
if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
break break
} }
} }
count++ count++
if !mulAsFloat { if !mulAsFloat {
if IsFloat(v) { if kern.IsFloat(v) {
mulAsFloat = true mulAsFloat = true
if mulAsFract { if mulAsFract {
floatProd = fractProd.toFloat() floatProd = fractProd.ToFloat()
} else { } else {
floatProd = float64(intProd) floatProd = float64(intProd)
} }
} else if !mulAsFract && isFraction(v) { } else if !mulAsFract && kern.IsFraction(v) {
fractProd = newFraction(intProd, 1) fractProd = kern.NewFraction(intProd, 1)
mulAsFract = true mulAsFract = true
} }
} }
if mulAsFloat { if mulAsFloat {
floatProd *= numAsFloat(v) floatProd *= kern.NumAsFloat(v)
} else if mulAsFract { } else if mulAsFract {
var item *FractionType var item *kern.FractionType
var ok bool var ok bool
if item, ok = v.(*FractionType); !ok { if item, ok = v.(*kern.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = newFraction(iv, 1) item = kern.NewFraction(iv, 1)
} }
fractProd = mulFract(fractProd, item) fractProd = kern.MulFract(fractProd, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intProd *= iv intProd *= iv
@@ -162,19 +164,19 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func mulFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
argv := args[ParamValue].([]any) argv := args[kern.ParamValue].([]any)
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1) result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
func ImportMathFuncs(ctx ExprContext) { func ImportMathFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{ ctx.RegisterFunc("add", kern.NewGolangFunctor(addFunc), kern.TypeNumber, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)), kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(0)),
}) })
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{ ctx.RegisterFunc("mul", kern.NewGolangFunctor(mulFunc), kern.TypeNumber, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)), kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(1)),
}) })
} }
+17 -15
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-os-file.go // builtin-os-file.go
@@ -8,6 +8,8 @@ import (
"fmt" "fmt"
"io" "io"
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern"
) )
const paramHandleOrPath = "handle-or-path" const paramHandleOrPath = "handle-or-path"
@@ -15,8 +17,8 @@ const fileReadTextIteratorType = "fileReadTextIterator"
type fileReadTextIterator struct { type fileReadTextIterator struct {
osReader *osReader osReader *osReader
index int index int64
count int count int64
line string line string
autoClose bool autoClose bool
} }
@@ -36,7 +38,7 @@ func (it *fileReadTextIterator) String() string {
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType) return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
} }
func (it *fileReadTextIterator) Count() int { func (it *fileReadTextIterator) Count() int64 {
return it.count return it.count
} }
@@ -60,7 +62,7 @@ func (it *fileReadTextIterator) Current() (item any, err error) {
return return
} }
func (it *fileReadTextIterator) Index() int { func (it *fileReadTextIterator) Index() int64 {
return it.index return it.index
} }
@@ -74,7 +76,7 @@ func (it *fileReadTextIterator) Reset() (err error) {
} }
func (it *fileReadTextIterator) HasOperation(name string) bool { func (it *fileReadTextIterator) HasOperation(name string) bool {
return slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName}, name) return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
} }
func (it *fileReadTextIterator) Clean() (err error) { func (it *fileReadTextIterator) Clean() (err error) {
@@ -88,25 +90,25 @@ func (it *fileReadTextIterator) Clean() (err error) {
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) { func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name { switch name {
case NextName: case kern.NextName:
v, err = it.Next() v, err = it.Next()
case ResetName: case kern.ResetName:
err = it.Reset() err = it.Reset()
case CleanName: case kern.CleanName:
err = it.Clean() err = it.Clean()
case IndexName: case kern.IndexName:
v = int64(it.Index()) v = int64(it.Index())
case CurrentName: case kern.CurrentName:
v, err = it.Current() v, err = it.Current()
case CountName: case kern.CountName:
v = it.count v = it.count
default: default:
err = errNoOperation(name) err = kern.ErrNoOperation(name)
} }
return return
} }
func fileReadIteratorFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *osReader var handle *osReader
var invalidFileHandle any var invalidFileHandle any
var ok, autoClose bool var ok, autoClose bool
@@ -115,7 +117,7 @@ func fileReadIteratorFunc(ctx ExprContext, name string, args map[string]any) (re
if handle, ok = args[paramHandleOrPath].(*osReader); !ok { if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 { if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
var handleAny any var handleAny any
if handleAny, err = openFileFunc(ctx, name, map[string]any{ParamFilepath: fileName}); err != nil { if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
return return
} }
if handleAny != nil { if handleAny != nil {
+41 -39
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-os-file.go // builtin-os-file.go
@@ -9,6 +9,8 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"git.portale-stac.it/go-pkg/expr/kern"
) )
const ( const (
@@ -65,8 +67,8 @@ func errInvalidFileHandle(funcName string, v any) error {
} }
} }
func createFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Create(filePath); err == nil { if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -77,8 +79,8 @@ func createFileFunc(ctx ExprContext, name string, args map[string]any) (result a
return return
} }
func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Open(filePath); err == nil { if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)} result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
@@ -89,8 +91,8 @@ func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any
return return
} }
func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil { if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -101,13 +103,13 @@ func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result a
return return
} }
func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[ParamHandle].(osHandle); !ok { if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -128,18 +130,18 @@ func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result an
return return
} }
func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[ParamHandle].(osHandle); !ok { if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
if w, ok := handle.(*osWriter); ok { if w, ok := handle.(*osWriter); ok {
if v, exists := args[ParamItem]; exists { if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any) argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...) result, err = fmt.Fprint(w.writer, argv...)
} }
@@ -154,14 +156,14 @@ func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (resul
return return
} }
func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil { if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[ParamHandle] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -194,14 +196,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result
return return
} }
func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil { if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[ParamHandle] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -220,39 +222,39 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (res
return return
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{ ctx.RegisterFunc("fileOpen", kern.NewGolangFunctor(openFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), kern.NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{ ctx.RegisterFunc("fileAppend", kern.NewGolangFunctor(appendFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), kern.NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{ ctx.RegisterFunc("fileCreate", kern.NewGolangFunctor(createFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), kern.NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("fileClose", kern.NewGolangFunctor(closeFileFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(ParamHandle), kern.NewFuncParam(kern.ParamHandle),
}) })
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("fileWriteText", kern.NewGolangFunctor(fileWriteTextFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParam(ParamHandle), kern.NewFuncParam(kern.ParamHandle),
NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""), kern.NewFuncParamFlagDef(kern.ParamItem, kern.PfDefault|kern.PfRepeat, ""),
}) })
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadText", kern.NewGolangFunctor(fileReadTextFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamHandle), kern.NewFuncParam(kern.ParamHandle),
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"), kern.NewFuncParamFlagDef(osLimitCh, kern.PfDefault, "\n"),
}) })
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadTextAll", kern.NewGolangFunctor(fileReadTextAllFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamHandle), kern.NewFuncParam(kern.ParamHandle),
}) })
ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{ ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
NewFuncParam(paramHandleOrPath), kern.NewFuncParam(paramHandleOrPath),
}) })
} }
+72 -70
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtin-string.go // builtin-string.go
@@ -8,6 +8,8 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
const ( const (
@@ -15,7 +17,7 @@ const (
) )
// --- Start of function definitions // --- Start of function definitions
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) { func doJoinStr(funcName string, sep string, it kern.Iterator) (result any, err error) {
var sb strings.Builder var sb strings.Builder
var v any var v any
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
@@ -25,7 +27,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
sb.WriteString(s) sb.WriteString(s)
} else { } else {
err = ErrExpectedGot(funcName, TypeString, v) err = kern.ErrExpectedGot(funcName, kern.TypeString, v)
return return
} }
} }
@@ -36,45 +38,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return return
} }
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if sep, ok := args[ParamSeparator].(string); ok { if sep, ok := args[kern.ParamSeparator].(string); ok {
if v, exists := args[ParamItem]; exists { if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any) argv := v.([]any)
if len(argv) == 1 { if len(argv) == 1 {
if ls, ok := argv[0].(*ListType); ok { if ls, ok := argv[0].(*kern.ListType); ok {
result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if it, ok := argv[0].(Iterator); ok { } else if it, ok := argv[0].(kern.Iterator); ok {
result, err = doJoinStr(name, sep, it) result, err = doJoinStr(name, sep, it)
} else if s, ok := argv[0].(string); ok { } else if s, ok := argv[0].(string); ok {
result = s result = s
} else { } else {
err = ErrInvalidParameterValue(name, ParamItem, v) err = kern.ErrInvalidParameterValue(name, kern.ParamItem, v)
} }
} else { } else {
result, err = doJoinStr(name, sep, NewArrayIterator(argv)) result, err = doJoinStr(name, sep, NewArrayIterator(argv))
} }
} }
} else { } else {
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator]) err = kern.ErrWrongParamType(name, kern.ParamSeparator, kern.TypeString, args[kern.ParamSeparator])
} }
return return
} }
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func subStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var start = 0 var start = 0
var count = -1 var count = -1
var source string var source string
var ok bool var ok bool
if source, ok = args[ParamSource].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil { if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
return return
} }
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil { if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
return return
} }
@@ -90,29 +92,29 @@ func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any,
return return
} }
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func trimStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source string
var ok bool var ok bool
if source, ok = args[ParamSource].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
result = strings.TrimSpace(source) result = strings.TrimSpace(source)
return return
} }
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func startsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source, prefix string var source, prefix string
var ok bool var ok bool
result = false result = false
if source, ok = args[ParamSource].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if prefix, ok = args[ParamPrefix].(string); !ok { if prefix, ok = args[kern.ParamPrefix].(string); !ok {
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix]) return result, kern.ErrWrongParamType(name, kern.ParamPrefix, kern.TypeString, args[kern.ParamPrefix])
} }
if strings.HasPrefix(source, prefix) { if strings.HasPrefix(source, prefix) {
result = true result = true
@@ -125,7 +127,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
break break
} }
} else { } else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec)) err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
break break
} }
} }
@@ -133,18 +135,18 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
return return
} }
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func endsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source, suffix string var source, suffix string
var ok bool var ok bool
result = false result = false
if source, ok = args[ParamSource].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if suffix, ok = args[ParamSuffix].(string); !ok { if suffix, ok = args[kern.ParamSuffix].(string); !ok {
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix]) return result, kern.ErrWrongParamType(name, kern.ParamSuffix, kern.TypeString, args[kern.ParamSuffix])
} }
if strings.HasPrefix(source, suffix) { if strings.HasPrefix(source, suffix) {
result = true result = true
@@ -157,7 +159,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
break break
} }
} else { } else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec)) err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
break break
} }
} }
@@ -165,24 +167,24 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
return return
} }
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func splitStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source, sep string var source, sep string
var count int = -1 var count int = -1
var parts []string var parts []string
var ok bool var ok bool
if source, ok = args[ParamSource].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if sep, ok = args[ParamSeparator].(string); !ok { if sep, ok = args[kern.ParamSeparator].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator]) return nil, fmt.Errorf("separator param must be string, got %s (%v)", kern.TypeName(args[kern.ParamSeparator]), args[kern.ParamSeparator])
} }
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt() if count64, ok := args[kern.ParamCount].(int64); ok { // TODO replace type assertion with toInt()
count = int(count64) count = int(count64)
} else { } else {
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount]) return nil, fmt.Errorf("part count must be integer, got %s (%v)", kern.TypeName(args[kern.ParamCount]), args[kern.ParamCount])
} }
if count > 0 { if count > 0 {
@@ -192,7 +194,7 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
} else { } else {
parts = []string{} parts = []string{}
} }
list := make(ListType, len(parts)) list := make(kern.ListType, len(parts))
for i, part := range parts { for i, part := range parts {
list[i] = part list[i] = part
} }
@@ -200,20 +202,20 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
return return
} }
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func upperStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok { if source, ok := args[kern.ParamSource].(string); ok {
result = strings.ToUpper(source) result = strings.ToUpper(source)
} else { } else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
return return
} }
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) { func lowerStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[ParamSource].(string); ok { if source, ok := args[kern.ParamSource].(string); ok {
result = strings.ToLower(source) result = strings.ToLower(source)
} else { } else {
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource]) err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
return return
} }
@@ -221,46 +223,46 @@ func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any
// --- End of function definitions // --- End of function definitions
// Import above functions in the context // Import above functions in the context
func ImportStringFuncs(ctx ExprContext) { func ImportStringFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strJoin", kern.NewGolangFunctor(joinStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSeparator), kern.NewFuncParam(kern.ParamSeparator),
NewFuncParamFlag(ParamItem, PfRepeat), kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
}) })
ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSub", kern.NewGolangFunctor(subStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)), kern.NewFuncParamFlagDef(kern.ParamStart, kern.PfDefault, int64(0)),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSplit", kern.NewGolangFunctor(splitStrFunc), "list of "+kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""), kern.NewFuncParamFlagDef(kern.ParamSeparator, kern.PfDefault, ""),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strTrim", kern.NewGolangFunctor(trimStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
}) })
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strStartsWith", kern.NewGolangFunctor(startsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
NewFuncParam(ParamPrefix), kern.NewFuncParam(kern.ParamPrefix),
NewFuncParamFlag(strParamOther, PfRepeat), kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
}) })
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strEndsWith", kern.NewGolangFunctor(endsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
NewFuncParam(ParamSuffix), kern.NewFuncParam(kern.ParamSuffix),
NewFuncParamFlag(strParamOther, PfRepeat), kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
}) })
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strUpper", kern.NewGolangFunctor(upperStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
}) })
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strLower", kern.NewGolangFunctor(lowerStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), kern.NewFuncParam(kern.ParamSource),
}) })
} }
+6 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// builtins-register.go // builtins-register.go
@@ -6,21 +6,23 @@ package expr
import ( import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type builtinModule struct { type builtinModule struct {
importFunc func(ExprContext) importFunc func(kern.ExprContext)
description string description string
imported bool imported bool
} }
func newBuiltinModule(importFunc func(ExprContext), description string) *builtinModule { func newBuiltinModule(importFunc func(kern.ExprContext), description string) *builtinModule {
return &builtinModule{importFunc, description, false} return &builtinModule{importFunc, description, false}
} }
var builtinModuleRegister map[string]*builtinModule var builtinModuleRegister map[string]*builtinModule
func RegisterBuiltinModule(name string, importFunc func(ExprContext), description string) { func RegisterBuiltinModule(name string, importFunc func(kern.ExprContext), description string) {
if builtinModuleRegister == nil { if builtinModuleRegister == nil {
builtinModuleRegister = make(map[string]*builtinModule) builtinModuleRegister = make(map[string]*builtinModule)
} }
+48 -46
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// data-cursors.go // data-cursors.go
@@ -7,24 +7,26 @@ package expr
import ( import (
"io" "io"
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type dataCursor struct { type dataCursor struct {
ds map[string]Functor ds map[string]kern.Functor
ctx ExprContext ctx kern.ExprContext
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor) initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
// cursorValid bool // true if resource is nil or if clean has not yet been called // cursorValid bool // true if resource is nil or if clean has not yet been called
index int index int64
count int count int64
current any current any
lastErr error lastErr error
resource any resource any
nextFunc Functor nextFunc kern.Functor
cleanFunc Functor cleanFunc kern.Functor
resetFunc Functor resetFunc kern.Functor
} }
func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) { func NewDataCursor(ctx kern.ExprContext, ds map[string]kern.Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
initState: true, initState: true,
@@ -35,14 +37,14 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
lastErr: nil, lastErr: nil,
resource: resource, resource: resource,
ctx: ctx.Clone(), ctx: ctx.Clone(),
nextFunc: ds[NextName], nextFunc: ds[kern.NextName],
cleanFunc: ds[CleanName], cleanFunc: ds[kern.CleanName],
resetFunc: ds[ResetName], resetFunc: ds[kern.ResetName],
} }
return return
} }
func (dc *dataCursor) Context() ExprContext { func (dc *dataCursor) Context() kern.ExprContext {
return dc.ctx return dc.ctx
} }
@@ -79,27 +81,27 @@ func (dc *dataCursor) String() string {
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) { func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name) exists = slices.Contains([]string{kern.CleanName, kern.ResetName, kern.CurrentName, kern.IndexName}, name)
if !exists { if !exists {
f, ok := dc.ds[name] f, ok := dc.ds[name]
exists = ok && isFunctor(f) exists = ok && kern.IsFunctor(f)
} }
return return
} }
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) { func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
if name == IndexName { if name == kern.IndexName {
value = int64(dc.Index()) value = int64(dc.Index())
} else if name == CleanName { } else if name == kern.CleanName {
err = dc.Clean() err = dc.Clean()
} else if name == ResetName { } else if name == kern.ResetName {
err = dc.Reset() err = dc.Reset()
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) { } else if functor, ok := dc.ds[name]; ok && kern.IsFunctor(functor) {
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
value, err = functor.InvokeNamed(ctx, name, args) value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx) kern.ExportObjects(dc.ctx, ctx)
} else { } else {
err = errNoOperation(name) err = kern.ErrNoOperation(name)
} }
return return
} }
@@ -127,10 +129,10 @@ func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any
func (dc *dataCursor) Reset() (err error) { func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil { if dc.resetFunc != nil {
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource}) actualParams := kern.BindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams) _, err = dc.resetFunc.InvokeNamed(ctx, kern.ResetName, actualParams)
exportObjects(dc.ctx, ctx) kern.ExportObjects(dc.ctx, ctx)
} }
dc.index = -1 dc.index = -1
dc.count = 0 dc.count = 0
@@ -142,10 +144,10 @@ func (dc *dataCursor) Reset() (err error) {
func (dc *dataCursor) Clean() (err error) { func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil { if dc.cleanFunc != nil {
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource}) actualParams := kern.BindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams) _, err = dc.cleanFunc.InvokeNamed(ctx, kern.CleanName, actualParams)
exportObjects(dc.ctx, ctx) kern.ExportObjects(dc.ctx, ctx)
} }
dc.lastErr = io.EOF dc.lastErr = io.EOF
return return
@@ -178,13 +180,13 @@ func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at
return return
} }
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) { func (dc *dataCursor) checkFilter(filter kern.Functor, item any) (accepted bool, err error) {
var v any var v any
var ok bool var ok bool
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
actualParams := bindActualParams(filter, []any{item, dc.index}) actualParams := kern.BindActualParams(filter, []any{item, dc.index})
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil { if v, err = filter.InvokeNamed(ctx, kern.FilterName, actualParams); err == nil && v != nil {
if accepted, ok = v.(bool); !ok { if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
} }
@@ -192,10 +194,10 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
return return
} }
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) { func (dc *dataCursor) mapItem(mapper kern.Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
actualParams := bindActualParams(mapper, []any{item, dc.index}) actualParams := kern.BindActualParams(mapper, []any{item, dc.index})
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams) mappedItem, err = mapper.InvokeNamed(ctx, kern.MapName, actualParams)
return return
} }
@@ -213,15 +215,15 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
return return
} }
current = dc.current current = dc.current
filter := dc.ds[FilterName] filter := dc.ds[kern.FilterName]
mapper := dc.ds[MapName] mapper := dc.ds[kern.MapName]
var item any var item any
for item == nil && dc.lastErr == nil { for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx) ctx := kern.CloneContext(dc.ctx)
dc.index++ dc.index++
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index}) actualParams := kern.BindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil { if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, kern.NextName, actualParams); dc.lastErr == nil {
if item == nil { if item == nil {
dc.lastErr = io.EOF dc.lastErr = io.EOF
} else { } else {
@@ -239,7 +241,7 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
} }
} }
} }
exportObjects(dc.ctx, ctx) kern.ExportObjects(dc.ctx, ctx)
} }
dc.current = item dc.current = item
if dc.lastErr != nil { if dc.lastErr != nil {
@@ -296,10 +298,10 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
// return // return
// } // }
func (dc *dataCursor) Index() int { func (dc *dataCursor) Index() int64 {
return dc.index - 1 return dc.index - 1
} }
func (dc *dataCursor) Count() int { func (dc *dataCursor) Count() int64 {
return dc.count return dc.count
} }
+33 -29
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// dict-iterator.go // dict-iterator.go
@@ -9,6 +9,8 @@ import (
"io" "io"
"slices" "slices"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type dictIterMode int type dictIterMode int
@@ -20,9 +22,9 @@ const (
) )
type DictIterator struct { type DictIterator struct {
a *DictType a *kern.DictType
count int count int64
index int index int64
keys []any keys []any
iterMode dictIterMode iterMode dictIterMode
} }
@@ -65,7 +67,7 @@ func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
} }
} }
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) { func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone var sortType = sortTypeNone
var s string var s string
var argAny any var argAny any
@@ -76,7 +78,7 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
} else { } else {
argAny = "default" argAny = "default"
} }
if s, err = ToGoString(argAny, "sort type"); err == nil { if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
switch strings.ToLower(s) { switch strings.ToLower(s) {
case "a", "asc": case "a", "asc":
sortType = sortTypeAsc sortType = sortTypeAsc
@@ -97,7 +99,7 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
argAny = "default" argAny = "default"
} }
if s, err = ToGoString(argAny, "iteration mode"); err == nil { if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
switch strings.ToLower(s) { switch strings.ToLower(s) {
case "k", "key", "keys": case "k", "key", "keys":
dictIt.iterMode = dictIterModeKeys dictIt.iterMode = dictIterModeKeys
@@ -114,20 +116,23 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
} }
} }
dictIt.makeKeys(*dict, sortType) if err == nil {
return dictIt, err dictIt.makeKeys(*dict, sortType)
it = dictIt
}
return
} }
func NewMapIterator(m map[any]any) (it *DictIterator) { func NewMapIterator(m map[any]any) (it *DictIterator) {
it = &DictIterator{a: (*DictType)(&m), count: 0, index: -1, keys: nil} it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil}
it.makeKeys(m, sortTypeNone) it.makeKeys(m, sortTypeNone)
return return
} }
func (it *DictIterator) String() string { func (it *DictIterator) String() string {
var l = 0 var l = int64(0)
if it.a != nil { if it.a != nil {
l = len(*it.a) l = int64(len(*it.a))
} }
return fmt.Sprintf("$({#%d})", l) return fmt.Sprintf("$({#%d})", l)
} }
@@ -137,46 +142,45 @@ func (it *DictIterator) TypeName() string {
} }
func (it *DictIterator) HasOperation(name string) bool { func (it *DictIterator) HasOperation(name string) bool {
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, kern.KeyName, kern.ValueName}, name)
yes := slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName, KeyName, ValueName}, name)
return yes return yes
} }
func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) { func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name { switch name {
case NextName: case kern.NextName:
v, err = it.Next() v, err = it.Next()
case ResetName: case kern.ResetName:
err = it.Reset() err = it.Reset()
case CleanName: case kern.CleanName:
err = it.Clean() err = it.Clean()
case IndexName: case kern.IndexName:
v = int64(it.Index()) v = int64(it.Index())
case CurrentName: case kern.CurrentName:
v, err = it.Current() v, err = it.Current()
case CountName: case kern.CountName:
v = it.count v = it.count
case KeyName: case kern.KeyName:
if it.index >= 0 && it.index < len(it.keys) { if it.index >= 0 && it.index < int64(len(it.keys)) {
v = it.keys[it.index] v = it.keys[it.index]
} else { } else {
err = io.EOF err = io.EOF
} }
case ValueName: case kern.ValueName:
if it.index >= 0 && it.index < len(it.keys) { if it.index >= 0 && it.index < int64(len(it.keys)) {
a := *(it.a) a := *(it.a)
v = a[it.keys[it.index]] v = a[it.keys[it.index]]
} else { } else {
err = io.EOF err = io.EOF
} }
default: default:
err = errNoOperation(name) err = kern.ErrNoOperation(name)
} }
return return
} }
func (it *DictIterator) Current() (item any, err error) { func (it *DictIterator) Current() (item any, err error) {
if it.index >= 0 && it.index < len(it.keys) { if it.index >= 0 && it.index < int64(len(it.keys)) {
switch it.iterMode { switch it.iterMode {
case dictIterModeKeys: case dictIterModeKeys:
item = it.keys[it.index] item = it.keys[it.index]
@@ -186,7 +190,7 @@ func (it *DictIterator) Current() (item any, err error) {
case dictIterModeItems: case dictIterModeItems:
a := *(it.a) a := *(it.a)
pair := []any{it.keys[it.index], a[it.keys[it.index]]} pair := []any{it.keys[it.index], a[it.keys[it.index]]}
item = newList(pair) item = kern.NewList(pair)
} }
} else { } else {
err = io.EOF err = io.EOF
@@ -202,11 +206,11 @@ func (it *DictIterator) Next() (item any, err error) {
return return
} }
func (it *DictIterator) Index() int { func (it *DictIterator) Index() int64 {
return it.index return it.index
} }
func (it *DictIterator) Count() int { func (it *DictIterator) Count() int64 {
return it.count return it.count
} }
+44
View File
@@ -1421,12 +1421,38 @@ Same as <<_add,add()>> but returns the product of the values of the parameters.
[green]`24` [green]`24`
==== Module "os.file" ==== Module "os.file"
The "os.file" module provides functions for working with files.
Activation: +
`{4sp}builtin "os.file"`
Currently available functions:
* <<_fileOpen,fileOpen()>>
* <<_fileAppend,fileAppend()>>
* <<_fileCreate,fileCreate()>>
* <<_fileClose,fileClose()>>
* <<_fileWriteText,fileWriteText()>>
* <<_fileReadText,fileReadText()>>
* <<_fileReadTextAll,fileReadTextAll()>>
More functions will be added in the future.
---
===== fileOpen() ===== fileOpen()
Syntax: +
`{4sp}fileOpen(<file-path>) -> file-handle`
Returns a file handle for the specified file path. The file is opened in read-write mode. If the file does not exist, it is created.
===== fileAppend() ===== fileAppend()
===== fileCreate() ===== fileCreate()
Syntax: +
`{4sp}fileCreate(<file-path>) -> file-handle`
Creates or truncates the named _<file-path>_. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0o666 (before umask). The associated file descriptor has mode [O_RDWR]. The directory containing the file must already exist.
===== fileClose() ===== fileClose()
@@ -1438,6 +1464,24 @@ Same as <<_add,add()>> but returns the product of the values of the parameters.
==== Module "string" ==== Module "string"
This module provides functions for working with strings.
Activation: +
`{4sp}builtin "string"`
Currently available functions:
* <<_strJoin,strJoin()>>
* <<_strSub,strSub()>>
* <<_strSplit,strSplit()>>
* <<_strTrim,strTrim()>>
* <<_strStartsWith,strStartsWith()>>
* <<_strEndsWith,strEndsWith()>>
* <<_strUpper,strUpper()>>
* <<_strLower,strLower()>>
---
===== strJoin() ===== strJoin()
Syntax: + Syntax: +
-386
View File
@@ -1,386 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package expr
import (
"fmt"
"strconv"
"strings"
)
// ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
// ---- Common functor definition
type baseFunctor struct {
info ExprFunc
}
func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil {
s = functor.info.ToString(opt)
} else {
s = "func(){}"
}
return s
}
func (functor *baseFunctor) GetParams() (params []ExprFuncParam) {
if functor.info != nil {
return functor.info.Params()
} else {
return []ExprFuncParam{}
}
}
func (functor *baseFunctor) SetFunc(info ExprFunc) {
functor.info = info
}
func (functor *baseFunctor) GetFunc() ExprFunc {
return functor.info
}
func (functor *baseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters
type paramFlags uint16
const (
PfDefault paramFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
name string
flags paramFlags
defaultValue any
}
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func NewFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
func (param *funcParamInfo) Name() string {
return param.name
}
func (param *funcParamInfo) Type() string {
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
return param.defaultValue
}
// --- Functions
// funcInfo implements ExprFunc
type funcInfo struct {
name string
minArgs int
maxArgs int
functor Functor
formalParams []ExprFuncParam
returnType string
}
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
var minArgs = 0
var maxArgs = 0
for _, p := range params {
if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
}
if p.IsDefault() || p.IsOptional() {
maxArgs++
} else if maxArgs == minArgs {
minArgs++
maxArgs++
} else {
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
}
if p.IsRepeat() {
minArgs--
maxArgs = -1
}
}
info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
}
functor.SetFunc(info)
return info, nil
}
func (info *funcInfo) Params() []ExprFuncParam {
return info.formalParams
}
func (info *funcInfo) ReturnType() string {
return info.returnType
}
func (info *funcInfo) ToString(opt FmtOpt) string {
var sb strings.Builder
if len(info.Name()) == 0 {
sb.WriteString("func")
} else {
sb.WriteString(info.Name())
}
sb.WriteByte('(')
if info.formalParams != nil {
for i, p := range info.formalParams {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(p.Name())
if p.IsDefault() {
sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"')
sb.WriteString(s)
sb.WriteByte('"')
} else {
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
}
}
}
}
if info.maxArgs < 0 {
sb.WriteString(" ...")
}
sb.WriteString("):")
if len(info.returnType) > 0 {
sb.WriteString(info.returnType)
} else {
sb.WriteString(TypeAny)
}
sb.WriteString("{}")
return sb.String()
}
func (info *funcInfo) Name() string {
return info.name
}
func (info *funcInfo) MinArgs() int {
return info.minArgs
}
func (info *funcInfo) MaxArgs() int {
return info.maxArgs
}
func (info *funcInfo) Functor() Functor {
return info.functor
}
func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
for i, tree := range callTerm.children {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.compute(paramCtx); err != nil {
break
}
if paramName, namedParam := getAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, len(callTerm.children)-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
// ----- Call a function ---
func getAssignVarName(t *term) (name string, ok bool) {
if ok = t.symbol() == SymEqual; ok {
name = t.children[0].source()
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
actualParams = bindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func bindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+66 -96
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// global-context.go // global-context.go
@@ -7,101 +7,68 @@ package expr
import ( import (
"path/filepath" "path/filepath"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
var globalCtx *SimpleStore //var globalCtx *SimpleStore
func ImportInContext(name string) (exists bool) { func ImportInContext(ctx kern.ExprContext, name string) (exists bool) {
var mod *builtinModule if globalCtx := ctx.GetGlobal(); globalCtx != nil {
if mod, exists = builtinModuleRegister[name]; exists { var mod *builtinModule
mod.importFunc(globalCtx) if mod, exists = builtinModuleRegister[name]; exists {
mod.imported = true mod.importFunc(globalCtx)
mod.imported = true
}
} }
return return
} }
func ImportInContextByGlobPattern(pattern string) (count int, err error) { func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count int, err error) {
var matched bool if globalCtx := ctx.GetGlobal(); globalCtx != nil {
for name, mod := range builtinModuleRegister { var matched bool
if matched, err = filepath.Match(pattern, name); err == nil { for name, mod := range builtinModuleRegister {
if matched { if matched, err = filepath.Match(pattern, name); err == nil {
count++ if matched {
mod.importFunc(globalCtx) count++
mod.imported = true mod.importFunc(globalCtx)
mod.imported = true
}
} else {
break
} }
} else {
break
} }
} }
return return
} }
func GetVar(ctx ExprContext, name string) (value any, exists bool) { func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
if value, exists = ctx.GetVar(name); !exists { if globalCtx := ctx.GetGlobal(); globalCtx != nil {
value, exists = globalCtx.GetVar(name) if !strings.HasPrefix(name, "_") {
} name = "_" + name
return
}
func GetLocalFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
var v any
if len(name) > 0 {
if v, exists = ctx.GetVar(name); exists && isFunctor(v) {
f, _ := v.(Functor)
item = f.GetFunc()
} else {
item, exists = ctx.GetFuncInfo(name)
} }
currentValue, _ = globalCtx.GetVar(name)
globalCtx.SetVar(name, newValue)
} }
return
}
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
// if len(name) > 0 {
// if item, exists = GetLocalFuncInfo(ctx, name); exists {
// ownerCtx = ctx
// } else if item, exists = globalCtx.GetFuncInfo(name); exists {
// ownerCtx = globalCtx
// }
// }
item, exists, _ = GetFuncInfoAndOwner(ctx, name)
return
}
func GetFuncInfoAndOwner(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
if len(name) > 0 {
if item, exists = GetLocalFuncInfo(ctx, name); exists {
ownerCtx = ctx
} else if item, exists = globalCtx.GetFuncInfo(name); exists {
ownerCtx = globalCtx
}
}
return
}
func GlobalCtrlSet(name string, newValue any) (currentValue any) {
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
currentValue, _ = globalCtx.GetVar(name)
globalCtx.SetVar(name, newValue)
return currentValue return currentValue
} }
func GlobalCtrlGet(name string) (currentValue any) { func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
if !strings.HasPrefix(name, "_") { if globalCtx := ctx.GetGlobal(); globalCtx != nil {
name = "_" + name if !strings.HasPrefix(name, "_") {
name = "_" + name
}
currentValue, _ = globalCtx.GetVar(name)
} }
currentValue, _ = globalCtx.GetVar(name)
return currentValue return currentValue
} }
func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) { func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") { if !strings.HasPrefix(name, "_") {
name = "_" + name name = "_" + name
} }
if v, exists := ctx.GetVar(name); exists && IsBool(v) { if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
@@ -109,11 +76,11 @@ func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
return currentStatus return currentStatus
} }
func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) { func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") { if !strings.HasPrefix(name, "_") {
name = "_" + name name = "_" + name
} }
if v, exists := ctx.GetVar(name); exists && IsBool(v) { if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
@@ -121,36 +88,39 @@ func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
return currentStatus return currentStatus
} }
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) { // func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
var v any // var v any
var exists bool // var exists bool
if !strings.HasPrefix(name, "_") { // if !strings.HasPrefix(name, "_") {
name = "_" + name // name = "_" + name
} // }
if v, exists = ctx.GetVar(name); !exists { // if v, exists = ctx.GetVar(name); !exists {
v, exists = globalCtx.GetVar(name) // v, exists = globalCtx.GetVar(name)
} // }
if exists { // if exists {
if b, ok := v.(bool); ok { // if b, ok := v.(bool); ok {
status = b // status = b
// }
// }
// return
// }
func getControlString(ctx kern.ExprContext, name string) (s string, exists bool) {
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
var v any
if v, exists = globalCtx.GetVar(name); exists {
s, exists = v.(string)
} }
} }
return return
} }
func getControlString(name string) (s string, exists bool) { func InitGlobal() (ctx kern.ExprContext) {
var v any ctx = NewSimpleStoreWithoutGlobalContext()
if v, exists = globalCtx.GetVar(name); exists { kern.InitDefaultVars(ctx)
s, exists = v.(string) ImportBuiltinsFuncs(ctx)
}
return return
} }
func init() {
globalCtx = NewSimpleStore()
initDefaultVars(globalCtx)
ImportBuiltinsFuncs(globalCtx)
}
+4 -2
View File
@@ -1,6 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
//go:build graph
// graph.go // graph.go
package expr package expr
@@ -53,7 +55,7 @@ func (r *Reticle) computeCharWidth() {
if v := ref.node.value(); v != nil { if v := ref.node.value(); v != nil {
ref.label = fmt.Sprintf("%v", v) ref.label = fmt.Sprintf("%v", v)
} else { } else {
ref.label = ref.node.source() ref.label = ref.node.Source()
} }
r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets
} }
+20 -16
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// helpers.go // helpers.go
@@ -9,13 +9,17 @@ import (
"io" "io"
"os" "os"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
"git.portale-stac.it/go-pkg/expr/util"
) )
func EvalString(ctx ExprContext, source string) (result any, err error) { func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
var tree *ast var tree *scan.Ast
r := strings.NewReader(source) r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations()) scanner := scan.NewScanner(r, scan.DefaultTranslations())
parser := NewParser() parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil { if tree, err = parser.Parse(scanner); err == nil {
@@ -34,21 +38,21 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
} }
func EvalStringV(source string, args []Arg) (result any, err error) { func EvalStringV(source string, args []Arg) (result any, err error) {
ctx := NewSimpleStore() ctx := NewSimpleStoreWithoutGlobalContext()
for _, arg := range args { for _, arg := range args {
if isFunc(arg.Value) { if util.IsFunc(arg.Value) {
if f, ok := arg.Value.(FuncTemplate); ok { if f, ok := arg.Value.(kern.FuncTemplate); ok {
functor := NewGolangFunctor(f) functor := kern.NewGolangFunctor(f)
// ctx.RegisterFunc(arg.Name, functor, 0, -1) // ctx.RegisterFunc(arg.Name, functor, 0, -1)
ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{ ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0), kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, 0),
}) })
} else { } else {
err = fmt.Errorf("invalid function specification: %q", arg.Name) err = fmt.Errorf("invalid function specification: %q", arg.Name)
} }
} else if integer, ok := anyInteger(arg.Value); ok { } else if integer, ok := kern.AnyInteger(arg.Value); ok {
ctx.SetVar(arg.Name, integer) ctx.SetVar(arg.Name, integer)
} else if float, ok := anyFloat(arg.Value); ok { } else if float, ok := kern.AnyFloat(arg.Value); ok {
ctx.SetVar(arg.Name, float) ctx.SetVar(arg.Name, float)
} else if _, ok := arg.Value.(string); ok { } else if _, ok := arg.Value.(string); ok {
ctx.SetVar(arg.Name, arg.Value) ctx.SetVar(arg.Name, arg.Value)
@@ -65,9 +69,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
return return
} }
func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) { func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
var tree *ast var tree *scan.Ast
scanner := NewScanner(r, DefaultTranslations()) scanner := scan.NewScanner(r, scan.DefaultTranslations())
parser := NewParser() parser := NewParser()
if tree, err = parser.Parse(scanner); err == nil { if tree, err = parser.Parse(scanner); err == nil {
@@ -76,7 +80,7 @@ func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
return return
} }
func EvalFile(ctx ExprContext, filePath string) (result any, err error) { func EvalFile(ctx kern.ExprContext, filePath string) (result any, err error) {
var fh *os.File var fh *os.File
if fh, err = os.Open(filePath); err != nil { if fh, err = os.Open(filePath); err != nil {
return nil, err return nil, err
+12 -9
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// import-utils.go // import-utils.go
@@ -11,6 +11,9 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/util"
) )
const ( const (
@@ -19,8 +22,8 @@ const (
) )
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) { if !(kern.IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, TypeName(paramValue)) err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
} }
return return
} }
@@ -45,8 +48,8 @@ func addEnvImportDirs(envVarName string, dirList []string) []string {
return dirList return dirList
} }
func addSearchDirs(endingPath string, dirList []string) []string { func addSearchDirs(ctx kern.ExprContext, endingPath string, dirList []string) []string {
if dirSpec, exists := getControlString(ControlSearchPath); exists { if dirSpec, exists := getControlString(ctx, kern.ControlSearchPath); exists {
dirs := strings.Split(dirSpec, ":") dirs := strings.Split(dirSpec, ":")
if dirList == nil { if dirList == nil {
dirList = dirs dirList = dirs
@@ -63,9 +66,9 @@ func addSearchDirs(endingPath string, dirList []string) []string {
return dirList return dirList
} }
func buildSearchDirList(endingPath, envVarName string) (dirList []string) { func buildSearchDirList(ctx kern.ExprContext, endingPath, envVarName string) (dirList []string) {
dirList = addEnvImportDirs(envVarName, dirList) dirList = addEnvImportDirs(envVarName, dirList)
dirList = addSearchDirs(endingPath, dirList) dirList = addSearchDirs(ctx, endingPath, dirList)
return return
} }
@@ -83,7 +86,7 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
} }
for _, dir := range dirList { for _, dir := range dirList {
if dir, err = ExpandPath(dir); err != nil { if dir, err = util.ExpandPath(dir); err != nil {
continue continue
} }
if fullPath := path.Join(dir, filename); isFile(fullPath) { if fullPath := path.Join(dir, filename); isFile(fullPath) {
@@ -106,7 +109,7 @@ func isPathRelative(filePath string) bool {
} }
func makeFilepath(filename string, dirList []string) (filePath string, err error) { func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if filename, err = ExpandPath(filename); err != nil { if filename, err = util.ExpandPath(filename); err != nil {
return return
} }
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// int-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
)
type IntIterator struct {
count int64
index int64
start int64
stop int64
step int64
}
func NewIntIterator(args []any) (it *IntIterator, err error) {
var argc int = 0
if args != nil {
argc = len(args)
}
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 {
return
}
if argc >= 2 {
it.start = it.stop
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil {
return
}
if argc >= 3 {
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil {
return
}
} else if it.start > it.stop {
it.step = -1
}
}
}
if it.step == 0 {
err = fmt.Errorf("step cannot be zero")
return
}
if it.start < it.stop && it.step < 0 {
err = fmt.Errorf("step cannot be negative when start < stop")
return
}
if it.start > it.stop && it.step > 0 {
err = fmt.Errorf("step cannot be positive when start > stop")
return
}
it.Reset()
return
}
func (it *IntIterator) String() string {
return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step)
}
func (it *IntIterator) TypeName() string {
return "IntIterator"
}
func (it *IntIterator) HasOperation(name string) bool {
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
return yes
}
func (it *IntIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
}
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
}
func (it *IntIterator) Next() (item any, err error) {
it.index += it.step
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *IntIterator) Index() int64 {
return it.index
}
func (it *IntIterator) Count() int64 {
return it.count
}
func (it *IntIterator) Reset() error {
it.index = it.start - it.step
it.count = 0
return nil
}
func (it *IntIterator) Clean() error {
return nil
}
+25 -6
View File
@@ -1,25 +1,44 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// iter-factory.go // iter-factory.go
package expr package expr
func NewIterator(value any) (it Iterator, err error) { import (
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
if value == nil { if value == nil {
return NewArrayIterator([]any{}), nil return NewArrayIterator([]any{}), nil
} }
switch v := value.(type) { switch v := value.(type) {
case *ListType: case *kern.ListType:
it = NewListIterator(v, nil) it = NewListIterator(v, nil)
case *DictType: case *kern.DictType:
it, err = NewDictIterator(v, nil) it, err = NewDictIterator(v, nil)
case []any: case []any:
it = NewArrayIterator(v) it = NewArrayIterator(v)
case Iterator: case kern.Iterator:
it = v // var exprs []*scan.Term
it, err = NewIterIter(v, ctx, ops)
default: default:
it = NewArrayIterator([]any{value}) it = NewArrayIterator([]any{value})
} }
return return
} }
func HasIterStandardOperations(name string) bool {
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
}
func HasIterOperations(name string, ops ...string) bool {
return slices.Contains([]string{
kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
}, name) ||
slices.Contains(ops, name)
}
+127
View File
@@ -0,0 +1,127 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iter-iter.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
type IterIter struct {
it kern.Iterator
count int64
index int64
ctx kern.ExprContext
exprList []*scan.Term
current any
}
func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) {
iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
return
}
func (it *IterIter) String() string {
return fmt.Sprintf("$(%s)", it.it)
}
func (it *IterIter) TypeName() string {
return "IterIter"
}
func (it *IterIter) HasOperation(name string) bool {
return HasIterStandardOperations(name)
}
func (it *IterIter) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
}
func (it *IterIter) Current() (item any, err error) {
if it.current != nil {
item = it.current
} else if len(it.exprList) > 0 {
// Evaluate the expression list and use the result as the current item
var exprValue any
for _, expr := range it.exprList {
if exprValue, err = expr.Compute(it.ctx); err != nil {
break
}
it.ctx.UnsafeSetVar(kern.ControlLastResult, exprValue)
}
if err == nil {
item = exprValue
}
} else {
var exists bool
if it.current, exists = it.ctx.GetVar("_"); !exists {
err = fmt.Errorf("current item not available")
} else {
item = it.current
}
}
return
}
func (it *IterIter) Next() (item any, err error) {
var src any
it.current = nil
ctx := it.ctx
for src, err = it.it.Next(); src == nil && err == nil; src, err = it.it.Next() {
}
if err == nil {
if src == nil {
err = io.EOF
} else {
ctx.UnsafeSetVar("_", src)
ctx.UnsafeSetVar("__", it.it.Index())
ctx.UnsafeSetVar("_#", it.it.Count())
item, err = it.Current()
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
}
}
return
}
func (it *IterIter) Index() int64 {
return it.index
}
func (it *IterIter) Count() int64 {
return it.count
}
func (it *IterIter) Reset() error {
it.index = -1
it.count = 0
return nil
}
func (it *IterIter) Clean() error {
return nil
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bind-go-function.go
package kern
// ---- Linking with Go functions
type GolangFunctor struct {
BaseFunctor
f FuncTemplate
}
func NewGolangFunctor(f FuncTemplate) *GolangFunctor {
return &GolangFunctor{f: f}
}
func (functor *GolangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *GolangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
func IsBool(v any) (ok bool) {
_, ok = v.(bool)
return ok
}
func ToBool(v any) (b bool, ok bool) {
ok = true
switch x := v.(type) {
case string:
b = len(x) > 0
case float64:
b = x != 0.0
case int64:
b = x != 0
case bool:
b = x
default:
ok = false
}
return
}
+6 -6
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// common-errors.go // common-errors.go
package expr package kern
import ( import (
"fmt" "fmt"
@@ -34,7 +34,7 @@ func ErrCantConvert(funcName string, value any, kind string) error {
} }
func ErrExpectedGot(funcName string, kind string, value any) error { func ErrExpectedGot(funcName string, kind string, value any) error {
return fmt.Errorf("%s(): expected %s, got %s (%v)", funcName, kind, TypeName(value), value) return fmt.Errorf("%s(): expected %s, got %s (%#v)", funcName, kind, TypeName(value), value)
} }
func ErrFuncDivisionByZero(funcName string) error { func ErrFuncDivisionByZero(funcName string) error {
@@ -52,7 +52,7 @@ func ErrFuncDivisionByZero(funcName string) error {
// } // }
func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error { func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName) return fmt.Errorf("%s(): invalid value %s (%#v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
} }
func undefArticle(s string) (article string) { func undefArticle(s string) (article string) {
@@ -86,6 +86,6 @@ func ErrUnknownVar(funcName, varName string) error {
// --- Operator errors // --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error { func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source()) return leftTerm.Errorf("left operand of %q must be a variable", opTerm.Source())
} }
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// common-params.go // common-params.go
package expr package kern
const ( const (
ParamArgs = "args" ParamArgs = "args"
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// common-type-names.go // common-type-names.go
package expr package kern
const ( const (
TypeAny = "any" TypeAny = "any"
+37
View File
@@ -0,0 +1,37 @@
// 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 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
}
@@ -1,10 +1,10 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// context-helpers.go // context-helpers.go
package expr package kern
func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) { func CloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
if sourceCtx != nil { if sourceCtx != nil {
clonedCtx = sourceCtx.Clone() clonedCtx = sourceCtx.Clone()
} }
@@ -25,8 +25,8 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
} }
func exportObjects(destCtx, sourceCtx ExprContext) { func ExportObjects(destCtx, sourceCtx ExprContext) {
exportAll := CtrlIsEnabled(sourceCtx, control_export_all) exportAll := CtrlIsEnabled(sourceCtx, ControlExportAll)
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll) // fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
// Export variables // Export variables
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) { for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
@@ -44,6 +44,6 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
func exportObjectsToParent(sourceCtx ExprContext) { func exportObjectsToParent(sourceCtx ExprContext) {
if parentCtx := sourceCtx.GetParent(); parentCtx != nil { if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
exportObjects(parentCtx, sourceCtx) ExportObjects(parentCtx, sourceCtx)
} }
} }
+27 -4
View File
@@ -1,8 +1,10 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// control.go // control.go
package expr package kern
import "strings"
// Preset control variables // Preset control variables
const ( const (
@@ -16,7 +18,7 @@ const (
// Other control variables // Other control variables
const ( const (
control_export_all = "_export_all" ControlExportAll = "_export_all"
) )
// Initial values // Initial values
@@ -24,13 +26,34 @@ const (
init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr" init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
) )
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
var v any
var exists bool
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
if v, exists = ctx.GetVar(name); !exists {
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
v, exists = globalCtx.GetVar(name)
}
}
if exists {
if b, ok := v.(bool); ok {
status = b
}
}
return
}
func SetCtrl(ctx ExprContext, name string, value any) (current any) { func SetCtrl(ctx ExprContext, name string, value any) (current any) {
current, _ = ctx.GetVar(name) current, _ = ctx.GetVar(name)
ctx.UnsafeSetVar(name, value) ctx.UnsafeSetVar(name, value)
return return
} }
func initDefaultVars(ctx ExprContext) { func InitDefaultVars(ctx ExprContext) {
if _, exists := ctx.GetVar(ControlPreset); exists { if _, exists := ctx.GetVar(ControlPreset); exists {
return return
} }
+36 -9
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// dict-type.go // dict-type.go
package expr package kern
import ( import (
"fmt" "fmt"
@@ -12,6 +12,11 @@ import (
type DictType map[any]any type DictType map[any]any
func IsDict(v any) (ok bool) {
_, ok = v.(*DictType)
return ok
}
func MakeDict() (dict *DictType) { func MakeDict() (dict *DictType) {
d := make(DictType) d := make(DictType)
dict = &d dict = &d
@@ -32,7 +37,7 @@ func NewDict(dictAny map[any]any) (dict *DictType) {
return return
} }
func newDict(dictAny map[any]*term) (dict *DictType) { func newDict(dictAny map[any]Term) (dict *DictType) {
// TODO Change with a call to NewDict() // TODO Change with a call to NewDict()
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
@@ -110,7 +115,7 @@ func (dict *DictType) ToString(opt FmtOpt) string {
sb.WriteString(": ") sb.WriteString(": ")
if formatter, ok := value.(Formatter); ok { if formatter, ok := value.(Formatter); ok {
sb.WriteString(formatter.ToString(opt)) sb.WriteString(formatter.ToString(opt))
} else if t, ok := value.(*term); ok { } else if t, ok := value.(Term); ok {
sb.WriteString(t.String()) sb.WriteString(t.String())
} else { } else {
sb.WriteString(fmt.Sprintf("%#v", value)) sb.WriteString(fmt.Sprintf("%#v", value))
@@ -129,7 +134,7 @@ func (dict *DictType) TypeName() string {
return "dict" return "dict"
} }
func (dict *DictType) hasKey(target any) (ok bool) { func (dict *DictType) HasKey(target any) (ok bool) {
for key := range *dict { for key := range *dict {
if ok = reflect.DeepEqual(key, target); ok { if ok = reflect.DeepEqual(key, target); ok {
break break
@@ -138,7 +143,16 @@ func (dict *DictType) hasKey(target any) (ok bool) {
return return
} }
func (dict *DictType) clone() (c *DictType) { func (dict *DictType) SetItem(key any, value any) {
(*dict)[key] = value
}
func (dict *DictType) GetItem(key any) (value any, exists bool) {
value, exists = (*dict)[key]
return
}
func (dict *DictType) Clone() (c *DictType) {
c = newDict(nil) c = newDict(nil)
for k, v := range *dict { for k, v := range *dict {
(*c)[k] = v (*c)[k] = v
@@ -146,7 +160,7 @@ func (dict *DictType) clone() (c *DictType) {
return return
} }
func (dict *DictType) merge(second *DictType) { func (dict *DictType) Merge(second *DictType) {
if second != nil { if second != nil {
for k, v := range *second { for k, v := range *second {
(*dict)[k] = v (*dict)[k] = v
@@ -154,8 +168,21 @@ func (dict *DictType) merge(second *DictType) {
} }
} }
func (dict *DictType) setItem(key any, value any) (err error) { func (dict *DictType) Equals(dict2 DictType) (answer bool) {
(*dict)[key] = value if dict2 != nil && len(*dict) == len(dict2) {
answer = true
for key, value1 := range *dict {
if value2, exists := dict2.GetItem(key); exists {
if !Equal(value1, value2) {
answer = false
break
}
} else {
answer = false
break
}
}
}
return return
} }
+4 -2
View File
@@ -1,14 +1,15 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// expr-context.go // expr-context.go
package expr package kern
// ----Expression Context // ----Expression Context
type ExprContext interface { type ExprContext interface {
Clone() ExprContext Clone() ExprContext
SetParent(ctx ExprContext) SetParent(ctx ExprContext)
GetParent() (ctx ExprContext) GetParent() (ctx ExprContext)
GetGlobal() (ctx ExprContext)
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
GetLast() any GetLast() any
SetVar(varName string, value any) SetVar(varName string, value any)
@@ -22,6 +23,7 @@ type ExprContext interface {
FuncCount() int FuncCount() int
DeleteFunc(funcName string) DeleteFunc(funcName string)
GetLocalFuncInfo(name string) (info ExprFunc, exists bool)
GetFuncInfo(name string) (item ExprFunc, exists bool) GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args map[string]any) (result any, err error) Call(name string, args map[string]any) (result any, err error)
RegisterFuncInfo(info ExprFunc) RegisterFuncInfo(info ExprFunc)
+7 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// expr-function.go // expr-function.go
package expr package kern
// ---- Functor interface // ---- Functor interface
type Functor interface { type Functor interface {
@@ -37,3 +37,8 @@ type ExprFunc interface {
PrepareCall(name string, actualParams map[string]any) (err error) PrepareCall(name string, actualParams map[string]any) (err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext) AllocContext(parentCtx ExprContext) (ctx ExprContext)
} }
func IsFunctor(v any) (ok bool) {
_, ok = v.(Functor)
return
}
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// expr.go // expr.go
package expr package kern
// ----Expression interface // ----Expression interface
type Expr interface { type Expr interface {
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// float.go
package kern
func IsFloat(v any) (ok bool) {
_, ok = v.(float64)
return ok
}
func AnyFloat(v any) (float float64, ok bool) {
ok = true
switch floatval := v.(type) {
case float32:
float = float64(floatval)
case float64:
float = floatval
default:
ok = false
}
return
}
+3 -3
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// formatter.go // formatter.go
package expr package kern
import "fmt" import "fmt"
@@ -46,7 +46,7 @@ type Formatter interface {
ToString(options FmtOpt) string ToString(options FmtOpt) string
} }
func getFormatted(v any, opt FmtOpt) (text string) { func GetFormatted(v any, opt FmtOpt) (text string) {
if v == nil { if v == nil {
text = "(nil)" text = "(nil)"
} else if s, ok := v.(string); ok { } else if s, ok := v.(string); ok {
+59 -36
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// fraction-type.go // fraction-type.go
package expr package kern
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html //https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
@@ -18,12 +18,12 @@ type FractionType struct {
num, den int64 num, den int64
} }
func newFraction(num, den int64) *FractionType { func NewFraction(num, den int64) *FractionType {
num, den = simplifyIntegers(num, den) num, den = simplifyIntegers(num, den)
return &FractionType{num, den} return &FractionType{num, den}
} }
func float64ToFraction(f float64) (fract *FractionType, err error) { func Float64ToFraction(f float64) (fract *FractionType, err error) {
var sign string var sign string
intPart, decPart := math.Modf(f) intPart, decPart := math.Modf(f)
if decPart < 0.0 { if decPart < 0.0 {
@@ -33,11 +33,11 @@ func float64ToFraction(f float64) (fract *FractionType, err error) {
} }
dec := fmt.Sprintf("%.12f", decPart) dec := fmt.Sprintf("%.12f", decPart)
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:]) s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
return makeGeneratingFraction(s) return MakeGeneratingFraction(s)
} }
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39 // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
func makeGeneratingFraction(s string) (f *FractionType, err error) { func MakeGeneratingFraction(s string) (f *FractionType, err error) {
var num, den int64 var num, den int64
var sign int64 = 1 var sign int64 = 1
var parts []string var parts []string
@@ -59,7 +59,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
return return
} }
if len(parts) == 1 { if len(parts) == 1 {
f = newFraction(sign*num, 1) f = NewFraction(sign*num, 1)
} else if len(parts) == 2 { } else if len(parts) == 2 {
subParts := strings.SplitN(parts[1], "(", 2) subParts := strings.SplitN(parts[1], "(", 2)
if len(subParts) == 1 { if len(subParts) == 1 {
@@ -76,7 +76,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
num = num*10 + int64(c-'0') num = num*10 + int64(c-'0')
den = den * 10 den = den * 10
} }
f = newFraction(sign*num, den) f = NewFraction(sign*num, den)
} else if len(subParts) == 2 { } else if len(subParts) == 2 {
sub := num sub := num
mul := int64(1) mul := int64(1)
@@ -103,7 +103,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
den *= mul den *= mul
} }
num -= sub num -= sub
f = newFraction(sign*num, den) f = NewFraction(sign*num, den)
} }
} }
exit: exit:
@@ -113,7 +113,15 @@ exit:
return return
} }
func (f *FractionType) toFloat() float64 { func (f *FractionType) N() int64 {
return f.num
}
func (f *FractionType) D() int64 {
return f.den
}
func (f *FractionType) ToFloat() float64 {
return float64(f.num) / float64(f.den) return float64(f.num) / float64(f.den)
} }
@@ -168,7 +176,7 @@ func (f *FractionType) TypeName() string {
// -------- fraction utility functions // -------- fraction utility functions
// greatest common divider // greatest common divider
func gcd(a, b int64) (g int64) { func Gcd(a, b int64) (g int64) {
if a < 0 { if a < 0 {
a = -a a = -a
} }
@@ -189,21 +197,21 @@ func gcd(a, b int64) (g int64) {
// lower common multiple // lower common multiple
func lcm(a, b int64) (l int64) { func lcm(a, b int64) (l int64) {
g := gcd(a, b) g := Gcd(a, b)
l = a * b / g l = a * b / g
return return
} }
// Sum two fractions // Sum two fractions
func sumFract(f1, f2 *FractionType) (sum *FractionType) { func SumFract(f1, f2 *FractionType) (sum *FractionType) {
m := lcm(f1.den, f2.den) m := lcm(f1.den, f2.den)
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m) sum = NewFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
return return
} }
// Multiply two fractions // Multiply two fractions
func mulFract(f1, f2 *FractionType) (prod *FractionType) { func MulFract(f1, f2 *FractionType) (prod *FractionType) {
prod = newFraction(f1.num*f2.num, f1.den*f2.den) prod = NewFraction(f1.num*f2.num, f1.den*f2.den)
return return
} }
@@ -212,11 +220,12 @@ func anyToFract(v any) (f *FractionType, err error) {
if f, ok = v.(*FractionType); !ok { if f, ok = v.(*FractionType); !ok {
if n, ok := v.(int64); ok { if n, ok := v.(int64); ok {
f = intToFraction(n) f = intToFraction(n)
} else if dec, ok := v.(float64); ok {
f, err = Float64ToFraction(dec)
} else {
err = ErrExpectedGot("fract", TypeFraction, v)
} }
} }
if f == nil {
err = ErrExpectedGot("fract", TypeFraction, v)
}
return return
} }
@@ -230,12 +239,12 @@ func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
return return
} }
func sumAnyFract(af1, af2 any) (sum any, err error) { func SumAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
} }
f := sumFract(f1, f2) f := SumFract(f1, f2)
if f.num == 0 { if f.num == 0 {
sum = 0 sum = 0
} else { } else {
@@ -250,7 +259,7 @@ func sumAnyFract(af1, af2 any) (sum any, err error) {
// =0 if af1 == af2 // =0 if af1 == af2
// >0 if af1 > af2 // >0 if af1 > af2
// err if af1 or af2 is not convertible to fraction // err if af1 or af2 is not convertible to fraction
func cmpAnyFract(af1, af2 any) (result int, err error) { func CmpAnyFract(af1, af2 any) (result int, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -265,25 +274,27 @@ func cmpAnyFract(af1, af2 any) (result int, err error) {
// =0 if af1 == af2 // =0 if af1 == af2
// >0 if af1 > af2 // >0 if af1 > af2
func cmpFract(f1, f2 *FractionType) (result int) { func cmpFract(f1, f2 *FractionType) (result int) {
f2.num = -f2.num if f1 != nil && f2 != nil {
f := sumFract(f1, f2) f2.num = -f2.num
if f.num < 0 { f := SumFract(f1, f2)
result = -1 if f.num < 0 {
} else if f.num > 0 { result = -1
result = 1 } else if f.num > 0 {
} else { result = 1
result = 0 } else {
result = 0
}
} }
return return
} }
func subAnyFract(af1, af2 any) (sum any, err error) { func SubAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
} }
f2.num = -f2.num f2.num = -f2.num
f := sumFract(f1, f2) f := SumFract(f1, f2)
if f.num == 0 { if f.num == 0 {
sum = 0 sum = 0
} else { } else {
@@ -292,7 +303,7 @@ func subAnyFract(af1, af2 any) (sum any, err error) {
return return
} }
func mulAnyFract(af1, af2 any) (prod any, err error) { func MulAnyFract(af1, af2 any) (prod any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -306,7 +317,7 @@ func mulAnyFract(af1, af2 any) (prod any, err error) {
return return
} }
func divAnyFract(af1, af2 any) (quot any, err error) { func DivAnyFract(af1, af2 any) (quot any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -345,7 +356,7 @@ func simplifyIntegers(num, den int64) (a, b int64) {
den = -den den = -den
num = -num num = -num
} }
g := gcd(num, den) g := Gcd(num, den)
a = num / g a = num / g
b = den / g b = den / g
return return
@@ -355,7 +366,19 @@ func intToFraction(n int64) *FractionType {
return &FractionType{n, 1} return &FractionType{n, 1}
} }
func isFraction(v any) (ok bool) { func IsFraction(v any) (ok bool) {
_, ok = v.(*FractionType) _, ok = v.(*FractionType)
return ok return ok
} }
// func IsFract(v any) (ok bool) {
// _, ok = v.(*FractionType)
// return ok
// }
func IsRational(v any) (ok bool) {
if _, ok = v.(*FractionType); !ok {
_, ok = v.(int64)
}
return ok
}
+164
View File
@@ -0,0 +1,164 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// func-info.go
package kern
import (
"fmt"
"strings"
)
// --- Functions
// FuncInfo implements expr.ExprFunc
type FuncInfo struct {
name string
minArgs int
maxArgs int
functor Functor
formalParams []ExprFuncParam
returnType string
}
func NewFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *FuncInfo, err error) {
var minArgs = 0
var maxArgs = 0
for _, p := range params {
if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
}
if p.IsDefault() || p.IsOptional() {
maxArgs++
} else if maxArgs == minArgs {
minArgs++
maxArgs++
} else {
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
}
if p.IsRepeat() {
minArgs--
maxArgs = -1
}
}
info = &FuncInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
}
functor.SetFunc(info)
return info, nil
}
func (info *FuncInfo) Params() []ExprFuncParam {
return info.formalParams
}
func (info *FuncInfo) ReturnType() string {
return info.returnType
}
func (info *FuncInfo) ToString(opt FmtOpt) string {
var sb strings.Builder
if len(info.Name()) == 0 {
sb.WriteString("func")
} else {
sb.WriteString(info.Name())
}
sb.WriteByte('(')
if info.formalParams != nil {
for i, p := range info.formalParams {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(p.Name())
if p.IsDefault() {
sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"')
sb.WriteString(s)
sb.WriteByte('"')
} else {
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
}
}
}
}
if info.maxArgs < 0 {
sb.WriteString(" ...")
}
sb.WriteString("):")
if len(info.returnType) > 0 {
sb.WriteString(info.returnType)
} else {
sb.WriteString(TypeAny)
}
sb.WriteString("{}")
return sb.String()
}
func (info *FuncInfo) Name() string {
return info.name
}
func (info *FuncInfo) MinArgs() int {
return info.minArgs
}
func (info *FuncInfo) MaxArgs() int {
return info.maxArgs
}
func (info *FuncInfo) Functor() Functor {
return info.functor
}
func (info *FuncInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *FuncInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func (info *FuncInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
+273
View File
@@ -0,0 +1,273 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package kern
import (
"fmt"
"strconv"
)
// ---- Function templates
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
type DeepFuncTemplate func(a, b any) (eq bool, err error)
// ---- Common functor definition
type BaseFunctor struct {
info ExprFunc
}
func (functor *BaseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil {
s = functor.info.ToString(opt)
} else {
s = "func(){}"
}
return s
}
func (functor *BaseFunctor) GetParams() (params []ExprFuncParam) {
if functor.info != nil {
return functor.info.Params()
} else {
return []ExprFuncParam{}
}
}
func (functor *BaseFunctor) SetFunc(info ExprFunc) {
functor.info = info
}
func (functor *BaseFunctor) GetFunc() ExprFunc {
return functor.info
}
func (functor *BaseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters
type FuncParamFlags uint16
const (
PfDefault FuncParamFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
name string
flags FuncParamFlags
defaultValue any
}
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func NewFuncParamFlag(name string, flags FuncParamFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func NewFuncParamFlagDef(name string, flags FuncParamFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
func (param *funcParamInfo) Name() string {
return param.name
}
func (param *funcParamInfo) Type() string {
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
return param.defaultValue
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm Term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
childCount := callTerm.GetChildCount()
for i := range childCount {
tree := callTerm.GetChild(i)
// for i, tree := range callTerm.Children() {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.Compute(paramCtx); err != nil {
break
}
if paramName, namedParam := GetAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, childCount-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), childCount)
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
// func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
// passedCount := len(actualParams)
// if info.MinArgs() > passedCount {
// err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
// return
// }
// if passedCount < len(info.formalParams) {
// for _, p := range info.formalParams {
// if _, exists := actualParams[p.Name()]; !exists {
// if !p.IsDefault() {
// break
// }
// if p.IsRepeat() {
// varArgs := make([]any, 1)
// varArgs[0] = p.DefaultValue()
// actualParams[p.Name()] = varArgs
// } else {
// actualParams[p.Name()] = p.DefaultValue()
// }
// }
// }
// }
// if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
// err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
// }
// return
// }
// ----- Call a function ---
// func getAssignVarName(t *term) (name string, ok bool) {
// if ok = t.symbol() == SymEqual; ok {
// name = t.children[0].source()
// }
// return
// }
func GetAssignVarName(t Term) (name string, ok bool) {
if ok = t.IsAssign(); ok {
name = t.GetChildSource(0)
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm Term) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
actualParams = BindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func BindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+9 -8
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// iterator.go // iterator.go
package expr package kern
import ( import (
// "errors" // "errors"
@@ -30,8 +30,8 @@ type Iterator interface {
fmt.Stringer fmt.Stringer
Next() (item any, err error) // must return io.EOF after the last item Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error) Current() (item any, err error)
Index() int Index() int64
Count() int Count() int64
HasOperation(name string) bool HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error) CallOperation(name string, args map[string]any) (value any, err error)
} }
@@ -42,10 +42,11 @@ type ExtIterator interface {
Clean() error Clean() error
} }
func errNoOperation(name string) error { func ErrNoOperation(name string) error {
return fmt.Errorf("no %s() function defined in the data-source", name) return fmt.Errorf("no %s() function defined in the data-source", name)
} }
// func errInvalidDataSource() error { func IsIterator(v any) (ok bool) {
// return errors.New("invalid data-source") _, ok = v.(Iterator)
// } return
}
+37 -47
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// list-type.go // list-type.go
package expr package kern
import ( import (
"fmt" "fmt"
@@ -12,23 +12,21 @@ import (
type ListType []any type ListType []any
func newListA(listAny ...any) (list *ListType) { func IsList(v any) (ok bool) {
_, ok = v.(*ListType)
return ok
}
func NewListA(listAny ...any) (list *ListType) {
if listAny == nil { if listAny == nil {
listAny = []any{} listAny = []any{}
} }
return newList(listAny)
}
func newList(listAny []any) (list *ListType) {
return NewList(listAny) return NewList(listAny)
} }
func NewList(listAny []any) (list *ListType) { func NewList(listAny []any) (list *ListType) {
if listAny != nil { if listAny != nil {
ls := make(ListType, len(listAny)) ls := make(ListType, len(listAny))
// for i, item := range listAny {
// ls[i] = item
// }
copy(ls, listAny) copy(ls, listAny)
list = &ls list = &ls
} }
@@ -52,13 +50,13 @@ func ListFromStrings(stringList []string) (list *ListType) {
return return
} }
func (ls *ListType) ToString(opt FmtOpt) (s string) { func (dict *ListType) ToString(opt FmtOpt) (s string) {
indent := GetFormatIndent(opt) indent := GetFormatIndent(opt)
flags := GetFormatFlags(opt) flags := GetFormatFlags(opt)
var sb strings.Builder var sb strings.Builder
sb.WriteByte('[') sb.WriteByte('[')
if len(*ls) > 0 { if len(*dict) > 0 {
innerOpt := MakeFormatOptions(flags, indent+1) innerOpt := MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1) nest := strings.Repeat(" ", indent+1)
@@ -66,7 +64,7 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
sb.WriteByte('\n') sb.WriteByte('\n')
sb.WriteString(nest) sb.WriteString(nest)
} }
for i, item := range []any(*ls) { for i, item := range []any(*dict) {
if i > 0 { if i > 0 {
if flags&MultiLine != 0 { if flags&MultiLine != 0 {
sb.WriteString(",\n") sb.WriteString(",\n")
@@ -98,30 +96,19 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
return return
} }
func (ls *ListType) String() string { func (dict *ListType) String() string {
return ls.ToString(0) return dict.ToString(0)
} }
func (ls *ListType) TypeName() string { func (dict *ListType) TypeName() string {
return "list" return "list"
} }
// func (list *ListType) indexDeepCmp(target any) (index int) { func (dict *ListType) Contains(t *ListType) (answer bool) {
// index = -1 if len(*dict) >= len(*t) {
// for i, item := range *list {
// if reflect.DeepEqual(item, target) {
// index = i
// break
// }
// }
// return
// }
func (ls *ListType) contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) {
answer = true answer = true
for _, item := range *t { for _, item := range *t {
if answer = ls.indexDeepSameCmp(item) >= 0; !answer { if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
break break
} }
} }
@@ -133,8 +120,11 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls1) == len(ls2) { if ls2 != nil && len(*ls1) == len(ls2) {
answer = true answer = true
for index, i1 := range *ls1 { for index, i1 := range *ls1 {
// if i1 != (ls2)[index] { // if !reflect.DeepEqual(i1, ls2[index]) {
if reflect.DeepEqual(i1, ls2[index]) { // answer = false
// break
// }
if !Equal(i1, ls2[index]) {
answer = false answer = false
break break
} }
@@ -143,12 +133,12 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
return return
} }
func (list *ListType) indexDeepSameCmp(target any) (index int) { func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
var eq bool var eq bool
var err error var err error
index = -1 index = -1
for i, item := range *list { for i, item := range *dict {
if eq, err = deepSame(item, target, sameContent); err != nil { if eq, err = deepSame(item, target, SameContent); err != nil {
break break
} else if eq { } else if eq {
index = i index = i
@@ -158,13 +148,13 @@ func (list *ListType) indexDeepSameCmp(target any) (index int) {
return return
} }
func sameContent(a, b any) (same bool, err error) { func SameContent(a, b any) (same bool, err error) {
la, _ := a.(*ListType) la, _ := a.(*ListType)
lb, _ := b.(*ListType) lb, _ := b.(*ListType)
if len(*la) == len(*lb) { if len(*la) == len(*lb) {
same = true same = true
for _, item := range *la { for _, item := range *la {
if pos := lb.indexDeepSameCmp(item); pos < 0 { if pos := lb.IndexDeepSameCmp(item); pos < 0 {
same = false same = false
break break
} }
@@ -173,19 +163,19 @@ func sameContent(a, b any) (same bool, err error) {
return return
} }
func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) { func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if IsNumOrFract(a) && IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if IsNumber(a) && IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if IsInteger(a) && IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
eq = li == ri eq = li == ri
} else { } else {
eq = numAsFloat(a) == numAsFloat(b) eq = NumAsFloat(a) == NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = CmpAnyFract(a, b); err == nil {
eq = cmp == 0 eq = cmp == 0
} }
} }
@@ -198,15 +188,15 @@ func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func (list *ListType) setItem(index int64, value any) (err error) { func (dict *ListType) SetItem(index int64, value any) (err error) {
if index >= 0 && index < int64(len(*list)) { if index >= 0 && index < int64(len(*dict)) {
(*list)[index] = value (*dict)[index] = value
} else { } else {
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*list)-1) err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
} }
return return
} }
func (list *ListType) appendItem(value any) { func (dict *ListType) AppendItem(value any) {
*list = append(*list, value) *dict = append(*dict, value)
} }
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// number.go
package kern
import (
"fmt"
)
func IsInteger(v any) (ok bool) {
_, ok = v.(int64)
return ok
}
func IsNumber(v any) (ok bool) {
return IsFloat(v) || IsInteger(v)
}
func IsNumOrFract(v any) (ok bool) {
return IsFloat(v) || IsInteger(v) || IsFraction(v)
}
func IsNumberString(v any) (ok bool) {
return IsString(v) || IsNumber(v)
}
func NumAsFloat(v any) (f float64) {
var ok bool
if f, ok = v.(float64); !ok {
if fract, ok := v.(*FractionType); ok {
f = fract.ToFloat()
} else {
i, _ := v.(int64)
f = float64(i)
}
}
return
}
func AnyInteger(v any) (i int64, ok bool) {
ok = true
switch intval := v.(type) {
case int:
i = int64(intval)
case uint8:
i = int64(intval)
case uint16:
i = int64(intval)
case uint64:
i = int64(intval)
case uint32:
i = int64(intval)
case int8:
i = int64(intval)
case int16:
i = int64(intval)
case int32:
i = int64(intval)
case int64:
i = intval
default:
ok = false
}
return
}
func ToGoInt(value any, description string) (i int, err error) {
if valueInt64, ok := value.(int64); ok {
i = int(valueInt64)
} else if valueInt, ok := value.(int); ok {
i = valueInt
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
func ToGoInt64(value any, description string) (i int64, err error) {
if valueInt64, ok := value.(int64); ok {
i = valueInt64
} else if valueInt, ok := value.(int); ok {
i = int64(valueInt)
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
import (
"fmt"
)
func IsString(v any) (ok bool) {
_, ok = v.(string)
return ok
}
func ToGoString(value any, description string) (s string, err error) {
if s, ok := value.(string); ok {
return s, nil
} else {
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
}
return
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// term.go
package kern
import (
"fmt"
)
type Term interface {
fmt.Stringer
// Children() []Term
Source() string
GetChildCount() (count int)
GetChild(index int) Term
GetChildSource(index int) string
Compute(ctx ExprContext) (result any, err error)
IsAssign() bool
Errorf(template string, args ...any) (err error)
}
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build darwin //go:build darwin
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// lib-ext-darwin.go // lib-ext-darwin.go
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build linux //go:build linux
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// lib-ext-linux.go // lib-ext-linux.go
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build windows //go:build windows
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// lib-ext-windows.go // lib-ext-windows.go
+31 -27
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// list-iterator.go // list-iterator.go
@@ -7,40 +7,43 @@ package expr
import ( import (
"fmt" "fmt"
"io" "io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type ListIterator struct { type ListIterator struct {
a *ListType a *kern.ListType
count int count int64
index int index int64
start int start int64
stop int stop int64
step int step int64
} }
func NewListIterator(list *ListType, args []any) (it *ListIterator) { func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
var argc int = 0 var argc int = 0
listLen := len(([]any)(*list)) listLen := int64(len(([]any)(*list)))
if args != nil { if args != nil {
argc = len(args) argc = len(args)
} }
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1} it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
if argc >= 1 { if argc >= 1 {
if i, err := ToGoInt(args[0], "start index"); err == nil { if i, err := kern.ToGoInt64(args[0], "start index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.start = i it.start = i
} }
if argc >= 2 { if argc >= 2 {
if i, err := ToGoInt(args[1], "stop index"); err == nil { if i, err := kern.ToGoInt64(args[1], "stop index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.stop = i it.stop = i
} }
if argc >= 3 { if argc >= 3 {
if i, err := ToGoInt(args[2], "step"); err == nil { if i, err := kern.ToGoInt64(args[2], "step"); err == nil {
if i < 0 { if i < 0 {
i = -i i = -i
} }
@@ -58,14 +61,14 @@ func NewListIterator(list *ListType, args []any) (it *ListIterator) {
} }
func NewArrayIterator(array []any) (it *ListIterator) { func NewArrayIterator(array []any) (it *ListIterator) {
it = &ListIterator{a: (*ListType)(&array), count: 0, index: -1, start: 0, stop: len(array) - 1, step: 1} it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1}
return return
} }
func (it *ListIterator) String() string { func (it *ListIterator) String() string {
var l = 0 var l = int64(0)
if it.a != nil { if it.a != nil {
l = len(*it.a) l = int64(len(*it.a))
} }
return fmt.Sprintf("$([#%d])", l) return fmt.Sprintf("$([#%d])", l)
} }
@@ -75,26 +78,27 @@ func (it *ListIterator) TypeName() string {
} }
func (it *ListIterator) HasOperation(name string) bool { func (it *ListIterator) HasOperation(name string) bool {
yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName //yes := name == expr.NextName || name == expr.ResetName || name == expr.IndexName || name == expr.CountName || name == expr.CurrentName
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
return yes return yes
} }
func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) { func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name { switch name {
case NextName: case kern.NextName:
v, err = it.Next() v, err = it.Next()
case ResetName: case kern.ResetName:
err = it.Reset() err = it.Reset()
case CleanName: case kern.CleanName:
err = it.Clean() err = it.Clean()
case IndexName: case kern.IndexName:
v = int64(it.Index()) v = int64(it.Index())
case CurrentName: case kern.CurrentName:
v, err = it.Current() v, err = it.Current()
case CountName: case kern.CountName:
v = it.count v = it.count
default: default:
err = errNoOperation(name) err = kern.ErrNoOperation(name)
} }
return return
} }
@@ -102,13 +106,13 @@ func (it *ListIterator) CallOperation(name string, args map[string]any) (v any,
func (it *ListIterator) Current() (item any, err error) { func (it *ListIterator) Current() (item any, err error) {
a := *(it.a) a := *(it.a)
if it.start <= it.stop { if it.start <= it.stop {
if it.stop < len(a) && it.index >= it.start && it.index <= it.stop { if it.stop < int64(len(a)) && it.index >= it.start && it.index <= it.stop {
item = a[it.index] item = a[it.index]
} else { } else {
err = io.EOF err = io.EOF
} }
} else { } else {
if it.start < len(a) && it.index >= it.stop && it.index <= it.start { if it.start < int64(len(a)) && it.index >= it.stop && it.index <= it.start {
item = a[it.index] item = a[it.index]
} else { } else {
err = io.EOF err = io.EOF
@@ -126,11 +130,11 @@ func (it *ListIterator) Next() (item any, err error) {
return return
} }
func (it *ListIterator) Index() int { func (it *ListIterator) Index() int64 {
return it.index return it.index
} }
func (it *ListIterator) Count() int { func (it *ListIterator) Count() int64 {
return it.count return it.count
} }
+17 -13
View File
@@ -1,29 +1,33 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-dict.go // operand-dict.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- dict term // -------- dict term
func newDictTerm(args map[any]*term) *term { func newDictTerm(args map[any]*scan.Term) *scan.Term {
return &term{ return &scan.Term{
tk: *NewValueToken(0, 0, SymDict, "{}", args), Tk: *scan.NewValueToken(0, 0, scan.SymDict, "{}", args),
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalDict, EvalFunc: evalDict,
} }
} }
// -------- dict func // -------- dict func
func evalDict(ctx ExprContext, opTerm *term) (v any, err error) { func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
dict, _ := opTerm.value().(map[any]*term) dict, _ := opTerm.Value().(map[any]*scan.Term)
items := make(DictType, len(dict)) items := make(kern.DictType, len(dict))
for key, tree := range dict { for key, tree := range dict {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
} }
items[key] = param items[key] = param
+20 -15
View File
@@ -1,30 +1,35 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-expr.go // operand-expr.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- expr term // -------- expr term
func newExprTerm(root *term) *term { func newExprTerm(root *scan.Term) *scan.Term {
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.source(), root) tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymExpression, root.Source(), root)
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalExpr, EvalFunc: evalExpr,
} }
} }
// -------- eval expr // -------- eval expr
func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) { func evalExpr(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if expr, ok := opTerm.value().(*term); ok { if ast, ok := opTerm.Value().(*scan.Term); ok {
v, err = expr.compute(ctx) v, err = ast.Compute(ctx)
} else { } else {
err = fmt.Errorf("expression expected, got %T", opTerm.value()) err = fmt.Errorf("expression expected, got %T", opTerm.Value())
} }
return return
} }
+42 -35
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-func.go // operand-func.go
@@ -6,27 +6,34 @@ package expr
import ( import (
"errors" "errors"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
// -------- function call term // -------- function call term
func newFuncCallTerm(tk *Token, args []*term) *term { func newFuncCallTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
return &term{ var pos scan.TermPosition = scan.PosLeaf
tk: *tk, if len(args) > 0 {
parent: nil, pos = scan.PosMultifix
children: args, }
position: posLeaf, return &scan.Term{
priority: priValue, Tk: *tk,
evalFunc: evalFuncCall, Parent: nil,
Children: args,
Position: pos,
Priority: scan.PriValue,
EvalFunc: evalFuncCall,
} }
} }
// -------- eval func call // -------- eval func call
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) { // func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
// name, _ := opTerm.tk.Value.(string) // name, _ := opTerm.Tk.Value.(string)
// params := make([]any, len(opTerm.children), len(opTerm.children)+5) // params := make([]any, len(opTerm.Children), len(opTerm.Children)+5)
// for i, tree := range opTerm.children { // for i, tree := range opTerm.Children {
// var param any // var param any
// if param, err = tree.compute(ctx); err != nil { // if param, err = tree.Compute(ctx); err != nil {
// break // break
// } // }
// params[i] = param // params[i] = param
@@ -38,42 +45,42 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
// return // return
// } // }
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) { func evalFuncCall(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
name, _ := opTerm.tk.Value.(string) name, _ := opTerm.Tk.Value.(string)
v, err = CallFunctionByTerm(ctx, name, opTerm) v, err = kern.CallFunctionByTerm(ctx, name, opTerm)
return return
} }
// -------- function definition term // -------- function definition term
func newFuncDefTerm(tk *Token, args []*term) *term { func newFuncDefTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, // value is the expression body Tk: *tk, // value is the expression body
parent: nil, Parent: nil,
children: args, // function params Children: args, // function params
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalFuncDef, EvalFunc: evalFuncDef,
} }
} }
// -------- eval func def // -------- eval func def
func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) { func evalFuncDef(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
bodySpec := opTerm.value() bodySpec := opTerm.Value()
if expr, ok := bodySpec.(*ast); ok { if ast, ok := bodySpec.(*scan.Ast); ok {
paramList := make([]ExprFuncParam, 0, len(opTerm.children)) paramList := make([]kern.ExprFuncParam, 0, len(opTerm.Children))
for _, param := range opTerm.children { for _, param := range opTerm.Children {
var defValue any var defValue any
flags := paramFlags(0) flags := kern.FuncParamFlags(0)
if len(param.children) > 0 { if len(param.Children) > 0 {
flags |= PfDefault flags |= kern.PfDefault
if defValue, err = param.children[0].compute(ctx); err != nil { if defValue, err = param.Children[0].Compute(ctx); err != nil {
return return
} }
} }
info := NewFuncParamFlagDef(param.source(), flags, defValue) info := kern.NewFuncParamFlagDef(param.Source(), flags, defValue)
paramList = append(paramList, info) paramList = append(paramList, info)
} }
v = newExprFunctor(expr, paramList, ctx) v = newExprFunctor(ast, paramList, ctx)
} else { } else {
err = errors.New("invalid function definition: the body specification must be an expression") err = errors.New("invalid function definition: the body specification must be an expression")
} }
+78 -39
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-iterator.go // operand-iterator.go
@@ -7,29 +7,32 @@ package expr
import ( import (
"slices" "slices"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
// -------- iterator term // -------- iterator term
func newIteratorTerm(tk *Token, args []*term) *term { func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
tk.Sym = SymIterator tk.Sym = scan.SymIterator
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: args, Children: args,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalIterator, EvalFunc: evalIterator,
} }
} }
// -------- eval iterator // -------- eval iterator
func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) { func evalTermArray(ctx kern.ExprContext, terms []*scan.Term) (values []any, err error) {
values = make([]any, len(terms)) values = make([]any, len(terms))
for i, t := range terms { for i, t := range terms {
var value any var value any
if value, err = t.compute(ctx); err == nil { if value, err = t.Compute(ctx); err == nil {
values[i] = value values[i] = value
} else { } else {
break break
@@ -38,25 +41,25 @@ func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
return return
} }
func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) { func evalFirstChild(ctx kern.ExprContext, iteratorTerm *scan.Term) (value any, err error) {
if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil { if len(iteratorTerm.Children) < 1 || iteratorTerm.Children[0] == nil {
err = iteratorTerm.Errorf("missing the data-source parameter") err = iteratorTerm.Errorf("missing the data-source parameter")
return return
} }
value, err = iteratorTerm.children[0].compute(ctx) value, err = iteratorTerm.Children[0].Compute(ctx)
return return
} }
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
if dictAny, ok := firstChildValue.(*DictType); ok { if dictAny, ok := firstChildValue.(*kern.DictType); ok {
requiredFields := []string{NextName} requiredFields := []string{kern.NextName}
fieldsMask := 0b1 fieldsMask := 0b1
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]kern.Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { if key, ok := keyAny.(string); ok {
if functor, ok := item.(Functor); ok { if functor, ok := item.(kern.Functor); ok {
ds[key] = functor ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 { if index := slices.Index(requiredFields, key); index >= 0 {
foundFields |= 1 << index foundFields |= 1 << index
@@ -72,15 +75,15 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
missingFields = append(missingFields, field) missingFields = append(missingFields, field)
} }
} }
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) err = iteratorTerm.Children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} }
return return
} }
func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) { func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var firstChildValue any var firstChildValue any
var ds map[string]Functor var ds map[string]kern.Functor
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil { if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
return return
@@ -95,24 +98,24 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
if len(ds) > 0 { if len(ds) > 0 {
var dc *dataCursor var dc *dataCursor
dcCtx := ctx.Clone() dcCtx := ctx.Clone()
if initFunc, exists := ds[InitName]; exists && initFunc != nil { if initFunc, exists := ds[kern.InitName]; exists && initFunc != nil {
var args []any var args []any
var resource any var resource any
if len(opTerm.children) > 1 { if len(opTerm.Children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil { if args, err = evalTermArray(ctx, opTerm.Children[1:]); err != nil {
return return
} }
} else { } else {
args = []any{} args = []any{}
} }
actualParams := bindActualParams(initFunc, args) actualParams := kern.BindActualParams(initFunc, args)
initCtx := ctx.Clone() initCtx := ctx.Clone()
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil { if resource, err = initFunc.InvokeNamed(initCtx, kern.InitName, actualParams); err != nil {
return return
} }
exportObjects(dcCtx, initCtx) kern.ExportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource) dc = NewDataCursor(dcCtx, ds, resource)
} else { } else {
dc = NewDataCursor(dcCtx, ds, nil) dc = NewDataCursor(dcCtx, ds, nil)
@@ -120,30 +123,66 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
v = dc v = dc
} else { } else {
if dictIt, ok := firstChildValue.(*DictType); ok { if dictIt, ok := firstChildValue.(*kern.DictType); ok {
var args []any var args []any
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v, err = NewDictIterator(dictIt, args) v, err = NewDictIterator(dictIt, args)
} }
} else { } else {
err = opTerm.children[0].Errorf("the data-source must be a dictionary") err = opTerm.Children[0].Errorf("the data-source must be a dictionary")
} }
} }
} else if list, ok := firstChildValue.(*ListType); ok { } else if list, ok := firstChildValue.(*kern.ListType); ok {
var args []any var args []any
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil { if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
v = NewListIterator(list, args) v = NewListIterator(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)
}
} else if it, ok := firstChildValue.(kern.Iterator); ok {
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
} else { } else {
var list []any var siblings []any
if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil { if siblings, err = evalSiblings(ctx, opTerm.Children, firstChildValue); err == nil {
v = NewArrayIterator(list) v = NewArrayIterator(siblings)
} }
} }
return return
} }
func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []any, err error) { // func evalIterIter(ctx kern.ExprContext, firstChildValue any, siblings []any) (v any, err error) {
// var op kern.Functor
// var args map[string]any
// if it, ok := firstChildValue.(kern.Iterator); ok {
// if len(siblings) > 1 {
// if op, ok = siblings[1].(kern.Functor); ok {
// args = make(map[string]any, len(siblings)-2)
// for i, arg := range siblings[2:] {
// switch a := arg.(type) {
// case *kern.DictType:
// for keyAny, item := range *a {
// if key, ok := keyAny.(string); ok {
// args[key] = item
// }
// }
// default:
// args["arg"+strconv.Itoa(i+1)] = arg
// }
// }
// } else if op == nil {
// return nil, fmt.Errorf("the first sibling parameter must be a functor to be used as operation for the iterator")
// }
// }
// v, err = NewIterIter(it, ctx, op, args)
// }
// return
// }
func evalSiblings(ctx kern.ExprContext, terms []*scan.Term, firstChildValue any) (list []any, err error) {
items := make([]any, 0, len(terms)) items := make([]any, 0, len(terms))
for i, tree := range terms { for i, tree := range terms {
var param any var param any
@@ -152,7 +191,7 @@ func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []an
continue continue
} }
param = firstChildValue param = firstChildValue
} else if param, err = tree.compute(ctx); err != nil { } else if param, err = tree.Compute(ctx); err != nil {
break break
} }
items = append(items, param) items = append(items, param)
+19 -14
View File
@@ -1,32 +1,37 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-list.go // operand-list.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- list term // -------- list term
func newListTermA(args ...*term) *term { func newListTermA(args ...*scan.Term) *scan.Term {
return newListTerm(0, 0, args) return newListTerm(0, 0, args)
} }
func newListTerm(row, col int, args []*term) *term { func newListTerm(row, col int, args []*scan.Term) *scan.Term {
return &term{ return &scan.Term{
tk: *NewValueToken(row, col, SymList, "[]", args), Tk: *scan.NewValueToken(row, col, scan.SymList, "[]", args),
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalList, EvalFunc: evalList,
} }
} }
// -------- list func // -------- list func
func evalList(ctx ExprContext, opTerm *term) (v any, err error) { func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
list, _ := opTerm.value().([]*term) list, _ := opTerm.Value().([]*scan.Term)
items := make(ListType, len(list)) items := make(kern.ListType, len(list))
for i, tree := range list { for i, tree := range list {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
} }
items[i] = param items[i] = param
+22 -17
View File
@@ -1,33 +1,38 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-literal.go // operand-literal.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- literal term // -------- literal term
func newLiteralTerm(tk *Token) *term { func newLiteralTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalLiteral, EvalFunc: evalLiteral,
} }
} }
// -------- eval func // -------- eval func
func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) { func evalLiteral(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
v = opTerm.tk.Value v = opTerm.Tk.Value
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymString, newLiteralTerm) scan.RegisterTermConstructor(scan.SymString, newLiteralTerm)
registerTermConstructor(SymInteger, newLiteralTerm) scan.RegisterTermConstructor(scan.SymInteger, newLiteralTerm)
registerTermConstructor(SymFloat, newLiteralTerm) scan.RegisterTermConstructor(scan.SymFloat, newLiteralTerm)
registerTermConstructor(SymFraction, newLiteralTerm) scan.RegisterTermConstructor(scan.SymFraction, newLiteralTerm)
registerTermConstructor(SymBool, newLiteralTerm) scan.RegisterTermConstructor(scan.SymBool, newLiteralTerm)
registerTermConstructor(SymKwNil, newLiteralTerm) scan.RegisterTermConstructor(scan.SymKwNil, newLiteralTerm)
} }
+19 -16
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-selector-case.go // operand-selector-case.go
@@ -7,19 +7,22 @@ package expr
import ( import (
"fmt" "fmt"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
// -------- selector case term // -------- selector case term
type selectorCase struct { type selectorCase struct {
filterList *term filterList *scan.Term
caseExpr Expr caseExpr kern.Expr
} }
func (sc *selectorCase) String() string { func (sc *selectorCase) String() string {
var sb strings.Builder var sb strings.Builder
if sc.filterList != nil { if sc.filterList != nil {
sc.filterList.toString(&sb) sc.filterList.ToString(&sb)
sb.WriteByte(' ') sb.WriteByte(' ')
} }
sb.WriteByte('{') sb.WriteByte('{')
@@ -28,23 +31,23 @@ func (sc *selectorCase) String() string {
return sb.String() return sb.String()
} }
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term { func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr}) tk := scan.NewValueToken(row, col, scan.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalSelectorCase, EvalFunc: evalSelectorCase,
} }
} }
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx ExprContext, opTerm *term) (v any, err error) { func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var ok bool var ok bool
if v, ok = opTerm.value().(*selectorCase); !ok { if v, ok = opTerm.Value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", opTerm.value()) err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
} }
return return
} }
+21 -16
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-var.go // operand-var.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- variable term // -------- variable term
func newVarTerm(tk *Token) *term { func newVarTerm(tk *scan.Token) *scan.Term {
t := &term{ t := &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalVar, EvalFunc: evalVar,
} }
t.tk.Sym = SymVariable t.Tk.Sym = scan.SymVariable
return t return t
} }
// -------- eval func // -------- eval func
func evalVar(ctx ExprContext, opTerm *term) (v any, err error) { func evalVar(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var exists bool var exists bool
name := opTerm.source() name := opTerm.Source()
if v, exists = GetVar(ctx, name); !exists { if v, exists = ctx.GetVar(name); !exists {
if info, exists := GetFuncInfo(ctx, name); exists { if info, exists := ctx.GetFuncInfo(name); exists {
v = info.Functor() v = info.Functor()
} else { } else {
err = fmt.Errorf("undefined variable or function %q", name) err = fmt.Errorf("undefined variable or function %q", name)
@@ -36,5 +41,5 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymIdentifier, newVarTerm) scan.RegisterTermConstructor(scan.SymIdentifier, newVarTerm)
} }
+94 -88
View File
@@ -1,34 +1,40 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserightChilded. // All rights reserightChilded.
// operator-assign.go // operator-assign.go
package expr 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/util"
)
//-------- assign term //-------- assign term
func newAssignTerm(tk *Token) (inst *term) { func newAssignTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priAssign, Priority: scan.PriAssign,
evalFunc: evalAssign, EvalFunc: evalAssign,
} }
} }
func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, value any) (err error) { func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
var collectionValue, keyListValue, keyValue any var collectionValue, keyListValue, keyValue any
var keyList *ListType var keyList *kern.ListType
var ok bool var ok bool
if collectionValue, err = collectionTerm.compute(ctx); err != nil { if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
return return
} }
if keyListValue, err = keyListTerm.compute(ctx); err != nil { if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return return
} }
if keyValue = (*keyList)[0]; keyValue == nil { if keyValue = (*keyList)[0]; keyValue == nil {
@@ -37,54 +43,54 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
} }
switch collection := collectionValue.(type) { switch collection := collectionValue.(type) {
case *ListType: case *kern.ListType:
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
err = collection.setItem(index, value) err = collection.SetItem(index, value)
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
} }
case *DictType: case *kern.DictType:
err = collection.setItem(keyValue, value) collection.SetItem(keyValue, value)
default: default:
err = collectionTerm.Errorf("collection expected") err = collectionTerm.Errorf("collection expected")
} }
return return
} }
func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) { func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) {
if leftTerm.symbol() == SymIndex { if leftTerm.Symbol() == scan.SymIndex {
err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v) err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
} else { } else {
ctx.UnsafeSetVar(leftTerm.source(), v) ctx.UnsafeSetVar(leftTerm.Source(), v)
} }
return return
} }
func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) { func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
leftTerm := opTerm.children[0] leftTerm := opTerm.Children[0]
leftSym := leftTerm.symbol() leftSym := leftTerm.Symbol()
if leftSym != SymVariable && leftSym != SymIndex { if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source) err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
return return
} }
rightChild := opTerm.children[1] rightChild := opTerm.Children[1]
if v, err = rightChild.compute(ctx); err == nil { if v, err = rightChild.Compute(ctx); err == nil {
if functor, ok := v.(Functor); ok { if functor, ok := v.(kern.Functor); ok {
if leftSym == SymVariable { if leftSym == scan.SymVariable {
if info := functor.GetFunc(); info != nil { if info := functor.GetFunc(); info != nil {
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
} else if funcDef, ok := functor.(*exprFunctor); ok { } else if funcDef, ok := functor.(*exprFunctor); ok {
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) paramSpecs := util.ForAll(funcDef.params, func(p kern.ExprFuncParam) kern.ExprFuncParam { return p })
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs) ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, paramSpecs)
} else { } else {
err = opTerm.Errorf("unknown function %s()", rightChild.source()) err = opTerm.Errorf("unknown function %s()", rightChild.Source())
} }
} else { } else {
err = assignValue(ctx, leftTerm, v) err = assignValue(ctx, leftTerm, v)
@@ -101,29 +107,29 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- assign term //-------- assign term
func newOpAssignTerm(tk *Token) (inst *term) { func newOpAssignTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priAssign, Priority: scan.PriAssign,
evalFunc: evalOpAssign, EvalFunc: evalOpAssign,
} }
} }
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) { func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) {
var collectionValue, keyListValue, keyValue any var collectionValue, keyListValue, keyValue any
var keyList *ListType var keyList *kern.ListType
var ok bool var ok bool
if collectionValue, err = collectionTerm.compute(ctx); err != nil { if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
return return
} }
if keyListValue, err = keyListTerm.compute(ctx); err != nil { if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return return
} }
if keyValue = (*keyList)[0]; keyValue == nil { if keyValue = (*keyList)[0]; keyValue == nil {
@@ -132,13 +138,13 @@ func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term)
} }
switch collection := collectionValue.(type) { switch collection := collectionValue.(type) {
case *ListType: case *kern.ListType:
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
value = (*collection)[index] value = (*collection)[index]
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
} }
case *DictType: case *kern.DictType:
value = (*collection)[keyValue] value = (*collection)[keyValue]
default: default:
err = collectionTerm.Errorf("collection expected") err = collectionTerm.Errorf("collection expected")
@@ -146,55 +152,55 @@ func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term)
return return
} }
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) { func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) {
if leftTerm.symbol() == SymIndex { if leftTerm.Symbol() == scan.SymIndex {
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1]) value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1])
} else { } else {
value, _ = ctx.GetVar(leftTerm.source()) value, _ = ctx.GetVar(leftTerm.Source())
} }
return return
} }
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) { func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue, leftValue any var rightValue, leftValue any
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
leftTerm := opTerm.children[0] leftTerm := opTerm.Children[0]
leftSym := leftTerm.symbol() leftSym := leftTerm.Symbol()
if leftSym != SymVariable && leftSym != SymIndex { if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source) err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
return return
} }
rightChild := opTerm.children[1] rightChild := opTerm.Children[1]
if rightValue, err = rightChild.compute(ctx); err == nil { if rightValue, err = rightChild.Compute(ctx); err == nil {
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil { if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
switch opTerm.symbol() { switch opTerm.Symbol() {
case SymPlusEqual: case scan.SymPlusEqual:
v, err = sumValues(opTerm, leftValue, rightValue) v, err = sumValues(opTerm, leftValue, rightValue)
case SymMinusEqual: case scan.SymMinusEqual:
v, err = diffValues(opTerm, leftValue, rightValue) v, err = diffValues(opTerm, leftValue, rightValue)
case SymStarEqual: case scan.SymStarEqual:
v, err = mulValues(opTerm, leftValue, rightValue) v, err = mulValues(opTerm, leftValue, rightValue)
case SymSlashEqual: case scan.SymSlashEqual:
v, err = divValues(opTerm, leftValue, rightValue) v, err = divValues(opTerm, leftValue, rightValue)
case SymPercEqual: case scan.SymPercEqual:
v, err = remainderValues(opTerm, leftValue, rightValue) v, err = remainderValues(opTerm, leftValue, rightValue)
case SymAmpersandEqual: case scan.SymAmpersandEqual:
v, err = bitwiseAnd(opTerm, leftValue, rightValue) v, err = bitwiseAnd(opTerm, leftValue, rightValue)
case SymVertBarEqual: case scan.SymVertBarEqual:
v, err = bitwiseOr(opTerm, leftValue, rightValue) v, err = bitwiseOr(opTerm, leftValue, rightValue)
case SymCaretEqual: case scan.SymCaretEqual:
v, err = bitwiseXor(opTerm, leftValue, rightValue) v, err = bitwiseXor(opTerm, leftValue, rightValue)
case SymDoubleLessEqual: case scan.SymDoubleLessEqual:
v, err = bitLeftShift(opTerm, leftValue, rightValue) v, err = bitLeftShift(opTerm, leftValue, rightValue)
case SymDoubleGreaterEqual: case scan.SymDoubleGreaterEqual:
v, err = bitRightShift(opTerm, leftValue, rightValue) v, err = bitRightShift(opTerm, leftValue, rightValue)
default: default:
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source()) err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
} }
if err == nil { if err == nil {
err = assignValue(ctx, leftTerm, v) err = assignValue(ctx, leftTerm, v)
@@ -206,15 +212,15 @@ func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymEqual, newAssignTerm) scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
registerTermConstructor(SymPlusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
registerTermConstructor(SymMinusEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
registerTermConstructor(SymStarEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
registerTermConstructor(SymSlashEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm)
registerTermConstructor(SymPercEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm)
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm)
registerTermConstructor(SymVertBarEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm)
registerTermConstructor(SymCaretEqual, newOpAssignTerm) scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm)
} }
+54 -49
View File
@@ -1,50 +1,55 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-bitwise.go // operator-bitwise.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- Bitwise NOT term //-------- Bitwise NOT term
func newBitwiseNotTerm(tk *Token) (inst *term) { func newBitwiseNotTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priBitwiseNot, Priority: scan.PriBitwiseNot,
evalFunc: evalBitwiseNot, EvalFunc: evalBitwiseNot,
} }
} }
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) { func evalBitwiseNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var value any var value any
if value, err = opTerm.evalPrefix(ctx); err != nil { if value, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if IsInteger(value) { if kern.IsInteger(value) {
i, _ := value.(int64) i, _ := value.(int64)
v = ^i v = ^i
} else { } else {
err = opTerm.errIncompatibleType(value) err = opTerm.ErrIncompatiblePrefixPostfixType(value)
} }
return return
} }
//-------- Bitwise AND term //-------- Bitwise AND term
func newBitwiseAndTerm(tk *Token) (inst *term) { func newBitwiseAndTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBitwiseAnd, Priority: scan.PriBitwiseAnd,
evalFunc: evalBitwiseAnd, EvalFunc: evalBitwiseAnd,
} }
} }
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) { func bitwiseAnd(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64 var leftInt, rightInt int64
var lok, rok bool var lok, rok bool
@@ -54,15 +59,15 @@ func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
if lok && rok { if lok && rok {
v = leftInt & rightInt v = leftInt & rightInt
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) { func evalBitwiseAnd(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
v, err = bitwiseAnd(opTerm, leftValue, rightValue) v, err = bitwiseAnd(opTerm, leftValue, rightValue)
@@ -71,17 +76,17 @@ func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- Bitwise OR term //-------- Bitwise OR term
func newBitwiseOrTerm(tk *Token) (inst *term) { func newBitwiseOrTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBitwiseOr, Priority: scan.PriBitwiseOr,
evalFunc: evalBitwiseOr, EvalFunc: evalBitwiseOr,
} }
} }
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) { func bitwiseOr(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64 var leftInt, rightInt int64
var lok, rok bool var lok, rok bool
@@ -91,15 +96,15 @@ func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
if lok && rok { if lok && rok {
v = leftInt | rightInt v = leftInt | rightInt
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) { func evalBitwiseOr(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -109,17 +114,17 @@ func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- Bitwise XOR term //-------- Bitwise XOR term
func newBitwiseXorTerm(tk *Token) (inst *term) { func newBitwiseXorTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBitwiseOr, Priority: scan.PriBitwiseXor,
evalFunc: evalBitwiseXor, EvalFunc: evalBitwiseXor,
} }
} }
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) { func bitwiseXor(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64 var leftInt, rightInt int64
var lok, rok bool var lok, rok bool
@@ -129,15 +134,15 @@ func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
if lok && rok { if lok && rok {
v = leftInt ^ rightInt v = leftInt ^ rightInt
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) { func evalBitwiseXor(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -147,8 +152,8 @@ func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymTilde, newBitwiseNotTerm) scan.RegisterTermConstructor(scan.SymTilde, newBitwiseNotTerm)
registerTermConstructor(SymAmpersand, newBitwiseAndTerm) scan.RegisterTermConstructor(scan.SymAmpersand, newBitwiseAndTerm)
registerTermConstructor(SymVertBar, newBitwiseOrTerm) scan.RegisterTermConstructor(scan.SymVertBar, newBitwiseOrTerm)
registerTermConstructor(SymCaret, newBitwiseXorTerm) scan.RegisterTermConstructor(scan.SymCaret, newBitwiseXorTerm)
} }
+67 -61
View File
@@ -1,52 +1,57 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-bool.go // operator-bool.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- NOT term //-------- NOT term
func newNotTerm(tk *Token) (inst *term) { func newNotTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priNot, Priority: scan.PriNot,
evalFunc: evalNot, EvalFunc: evalNot,
} }
} }
func evalNot(ctx ExprContext, opTerm *term) (v any, err error) { func evalNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = opTerm.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if b, ok := ToBool(rightValue); ok { if b, ok := kern.ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = opTerm.errIncompatibleType(rightValue) err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
} }
return return
} }
//-------- AND term //-------- AND term
func newAndTerm(tk *Token) (inst *term) { func newAndTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priAnd, Priority: scan.PriAnd,
evalFunc: evalAnd, EvalFunc: evalAnd,
} }
} }
func evalAnd(ctx ExprContext, self *term) (v any, err error) { func evalAnd(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
v, err = evalAndWithShortcut(ctx, self) v, err = evalAndWithShortcut(ctx, self)
} else { } else {
v, err = evalAndWithoutShortcut(ctx, self) v, err = evalAndWithoutShortcut(ctx, self)
@@ -54,47 +59,48 @@ func evalAnd(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { func evalAndWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var leftBool, rightBool bool var leftBool, rightBool bool
var lok, rok bool var lok, rok bool
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
return return
} }
leftBool, lok = ToBool(leftValue) leftBool, lok = kern.ToBool(leftValue)
rightBool, rok = ToBool(rightValue) rightBool, rok = kern.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool && rightBool v = leftBool && rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) { func evalAndWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = self.CheckOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = self.Children[0].Compute(ctx); err != nil {
return return
} }
if leftBool, lok := ToBool(leftValue); !lok { if leftBool, lok := kern.ToBool(leftValue); !lok {
err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue)) // err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
return // return
err = self.ErrIncompatibleType(leftValue, "left")
} else if !leftBool { } else if !leftBool {
v = false v = false
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
if rightBool, rok := ToBool(rightValue); rok { if rightBool, rok := kern.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
} }
} }
return return
@@ -102,18 +108,18 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
//-------- OR term //-------- OR term
func newOrTerm(tk *Token) (inst *term) { func newOrTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priOr, Priority: scan.PriOr,
evalFunc: evalOr, EvalFunc: evalOr,
} }
} }
func evalOr(ctx ExprContext, self *term) (v any, err error) { func evalOr(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
v, err = evalOrWithShortcut(ctx, self) v, err = evalOrWithShortcut(ctx, self)
} else { } else {
v, err = evalOrWithoutShortcut(ctx, self) v, err = evalOrWithoutShortcut(ctx, self)
@@ -121,47 +127,47 @@ func evalOr(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { func evalOrWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var leftBool, rightBool bool var leftBool, rightBool bool
var lok, rok bool var lok, rok bool
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
return return
} }
leftBool, lok = ToBool(leftValue) leftBool, lok = kern.ToBool(leftValue)
rightBool, rok = ToBool(rightValue) rightBool, rok = kern.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool || rightBool v = leftBool || rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) { func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = self.CheckOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = self.Children[0].Compute(ctx); err != nil {
return return
} }
if leftBool, lok := ToBool(leftValue); !lok { if leftBool, lok := kern.ToBool(leftValue); !lok {
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue)) err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
return return
} else if leftBool { } else if leftBool {
v = true v = true
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
if rightBool, rok := ToBool(rightValue); rok { if rightBool, rok := kern.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.ErrIncompatibleTypes(leftValue, rightValue)
} }
} }
return return
@@ -169,7 +175,7 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymNot, newNotTerm) scan.RegisterTermConstructor(scan.SymNot, newNotTerm)
registerTermConstructor(SymAnd, newAndTerm) scan.RegisterTermConstructor(scan.SymAnd, newAndTerm)
registerTermConstructor(SymOr, newOrTerm) scan.RegisterTermConstructor(scan.SymOr, newOrTerm)
} }
+23 -18
View File
@@ -1,50 +1,55 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-builtin.go // operator-builtin.go
package expr package expr
import "io" import (
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- builtin term //-------- builtin term
func newBuiltinTerm(tk *Token) (inst *term) { func newBuiltinTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalBuiltin, EvalFunc: evalBuiltin,
} }
} }
func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) { func evalBuiltin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsString(childValue) { if kern.IsString(childValue) {
module, _ := childValue.(string) module, _ := childValue.(string)
count, err = ImportInContextByGlobPattern(module) count, err = ImportInContextByGlobPattern(ctx, module)
} else { } else {
var moduleSpec any var moduleSpec any
var it Iterator var it kern.Iterator
if it, err = NewIterator(childValue); err != nil { if it, err = NewIterator(ctx, childValue, nil); err != nil {
return return
} }
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok { if module, ok := moduleSpec.(string); ok {
if ImportInContext(module) { if ImportInContext(ctx, module) {
count++ count++
} else { } else {
err = opTerm.Errorf("unknown builtin module %q", module) err = opTerm.Errorf("unknown builtin module %q", module)
break break
} }
} else { } else {
err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, kern.TypeName(moduleSpec))
break break
} }
} }
@@ -60,5 +65,5 @@ func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymKwBuiltin, newBuiltinTerm) scan.RegisterTermConstructor(scan.SymKwBuiltin, newBuiltinTerm)
} }
+16 -11
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-but.go // operator-but.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- but term //-------- but term
func newButTerm(tk *Token) (inst *term) { func newButTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBut, Priority: scan.PriBut,
evalFunc: evalBut, EvalFunc: evalBut,
} }
} }
func evalBut(ctx ExprContext, opTerm *term) (v any, err error) { func evalBut(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
_, v, err = opTerm.evalInfix(ctx) _, v, err = opTerm.EvalInfix(ctx)
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymKwBut, newButTerm) scan.RegisterTermConstructor(scan.SymKwBut, newButTerm)
} }
+23 -18
View File
@@ -1,39 +1,44 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-context-value.go // operator-context-value.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- context term //-------- context term
func newContextTerm(tk *Token) (inst *term) { func newContextTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priIncDec, Priority: scan.PriIncDec,
evalFunc: evalContextValue, EvalFunc: evalContextValue,
} }
} }
func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) { func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
var sourceCtx ExprContext var sourceCtx kern.ExprContext
if len(opTerm.children) == 0 { if len(opTerm.Children) == 0 {
sourceCtx = ctx sourceCtx = ctx
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" { } else if opTerm.Children[0].Symbol() == scan.SymVariable && opTerm.Children[0].Source() == "global" {
sourceCtx = globalCtx sourceCtx = ctx.GetGlobal()
} else if childValue, err = opTerm.evalPrefix(ctx); err == nil { } else if childValue, err = opTerm.EvalPrefix(ctx); err == nil {
if dc, ok := childValue.(*dataCursor); ok { if dc, ok := childValue.(*dataCursor); ok {
sourceCtx = dc.ctx sourceCtx = dc.ctx
} }
} }
if sourceCtx != nil { if sourceCtx != nil {
if formatter, ok := sourceCtx.(DictFormat); ok { if formatter, ok := sourceCtx.(kern.DictFormat); ok {
v = formatter.ToDict() v = formatter.ToDict()
} else if formatter, ok := sourceCtx.(Formatter); ok { } else if formatter, ok := sourceCtx.(kern.Formatter); ok {
v = formatter.ToString(0) v = formatter.ToString(0)
} else { } else {
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) // keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
@@ -49,12 +54,12 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
v = d v = d
} }
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleDollar, newContextTerm) scan.RegisterTermConstructor(scan.SymDoubleDollar, newContextTerm)
} }
+16 -11
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-ctrl.go // operator-ctrl.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- export all term //-------- export all term
func newExportAllTerm(tk *Token) (inst *term) { func newExportAllTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: nil, Children: nil,
position: posLeaf, Position: scan.PosLeaf,
priority: priValue, Priority: scan.PriValue,
evalFunc: evalExportAll, EvalFunc: evalExportAll,
} }
} }
func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) { func evalExportAll(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, kern.ControlExportAll)
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleAt, newExportAllTerm) scan.RegisterTermConstructor(scan.SymDoubleAt, newExportAllTerm)
} }
+55 -50
View File
@@ -1,38 +1,43 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-default.go // operator-default.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- default term //-------- default term
func newDefaultTerm(tk *Token) (inst *term) { func newDefaultTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priDefault, Priority: scan.PriDefault,
evalFunc: evalDefault, EvalFunc: evalDefault,
} }
} }
func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) { func evalDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue any var rightValue any
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
leftTerm := opTerm.children[0] leftTerm := opTerm.Children[0]
if leftTerm.tk.Sym != SymVariable { if leftTerm.Tk.Sym != scan.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
} }
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists { if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
v = leftValue v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil { } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
v = rightValue v = rightValue
} }
return return
@@ -40,32 +45,32 @@ func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- alternate term //-------- alternate term
func newAlternateTerm(tk *Token) (inst *term) { func newAlternateTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priDefault, Priority: scan.PriDefault,
evalFunc: evalAlternate, EvalFunc: evalAlternate,
} }
} }
func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) { func evalAlternate(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue any var rightValue any
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
leftTerm := opTerm.children[0] leftTerm := opTerm.Children[0]
if leftTerm.tk.Sym != SymVariable { if leftTerm.Tk.Sym != scan.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
} }
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil { if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists && leftValue != nil {
if rightValue, err = opTerm.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
v = rightValue v = rightValue
} }
} else { } else {
@@ -76,41 +81,41 @@ func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- default assign term //-------- default assign term
func newDefaultAssignTerm(tk *Token) (inst *term) { func newDefaultAssignTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priDefault, Priority: scan.PriDefault,
evalFunc: evalAssignDefault, EvalFunc: evalAssignDefault,
} }
} }
func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) { func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue any var rightValue any
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
leftTerm := opTerm.children[0] leftTerm := opTerm.Children[0]
if leftTerm.tk.Sym != SymVariable { if leftTerm.Tk.Sym != scan.SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) // err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm) err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return return
} }
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists { if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
v = leftValue v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil { } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok { if functor, ok := rightValue.(kern.Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) //ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{ ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat), kern.NewFuncParamFlag(kern.ParamValue, kern.PfDefault|kern.PfRepeat),
}) })
} else { } else {
v = rightValue v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue) ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
} }
} }
return return
@@ -118,7 +123,7 @@ func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleQuestion, newDefaultTerm) scan.RegisterTermConstructor(scan.SymDoubleQuestion, newDefaultTerm)
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm) scan.RegisterTermConstructor(scan.SymQuestionEqual, newDefaultAssignTerm)
registerTermConstructor(SymQuestionExclam, newAlternateTerm) scan.RegisterTermConstructor(scan.SymQuestionExclam, newAlternateTerm)
} }
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-digest.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- digest term
func newDigestTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalDigest,
}
}
func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item, lastValue any
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
lastValue = nil
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 {
if rightValue == nil {
break
} else {
lastValue = rightValue
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = lastValue
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwDigest, newDigestTerm)
}
+40 -19
View File
@@ -1,36 +1,41 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-dot.go // operator-dot.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- dot term // -------- dot term
func newDotTerm(tk *Token) (inst *term) { func newDotTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priDot, Priority: scan.PriDot,
evalFunc: evalDot, EvalFunc: evalDot,
} }
} }
func evalDot(ctx ExprContext, opTerm *term) (v any, err error) { func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
if leftValue, err = opTerm.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return return
} }
indexTerm := opTerm.children[1] indexTerm := opTerm.Children[1]
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case ExtIterator: case kern.ExtIterator:
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ { if indexTerm.Tk.Sym == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
opName := indexTerm.source() opName := indexTerm.Source()
if unboxedValue.HasOperation(opName) { if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, map[string]any{}) v, err = unboxedValue.CallOperation(opName, map[string]any{})
} else { } else {
@@ -38,11 +43,27 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
v = false v = false
} }
} else { } else {
err = indexTerm.tk.ErrorExpectedGot("identifier") err = indexTerm.Tk.ErrorExpectedGot("identifier")
}
case *kern.DictType:
var ok bool
s := opTerm.Children[1].Tk.Sym
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)
}
} }
default: default:
if rightValue, err = opTerm.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
} }
} }
return return
@@ -50,5 +71,5 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymDot, newDotTerm) scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
} }
+19 -14
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-fact.go // operator-fact.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- fact term //-------- fact term
func newFactTerm(tk *Token) (inst *term) { func newFactTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPostfix, Position: scan.PosPostfix,
priority: priFact, Priority: scan.PriFact,
evalFunc: evalFact, EvalFunc: evalFact,
} }
} }
func evalFact(ctx ExprContext, opTerm *term) (v any, err error) { func evalFact(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue any var leftValue any
if leftValue, err = opTerm.evalPrefix(ctx); err != nil { if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return return
} }
if IsInteger(leftValue) { if kern.IsInteger(leftValue) {
if i, _ := leftValue.(int64); i >= 0 { if i, _ := leftValue.(int64); i >= 0 {
f := int64(1) f := int64(1)
for k := int64(1); k <= i; k++ { for k := int64(1); k <= i; k++ {
@@ -36,12 +41,12 @@ func evalFact(ctx ExprContext, opTerm *term) (v any, err error) {
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i) err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
} }
} else { } else {
err = opTerm.errIncompatibleType(leftValue) err = opTerm.Errorf("incompatible type for factorial: %s", kern.TypeName(leftValue))
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymExclamation, newFactTerm) scan.RegisterTermConstructor(scan.SymExclamation, newFactTerm)
} }
+78
View File
@@ -0,0 +1,78 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-filter.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- map term
func newFilterTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalFilter,
}
}
func evalFilter(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
var ok bool
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if it, ok = leftValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of FILTER must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
}
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 {
if success, valid := kern.ToBool(rightValue); valid {
if success {
values.AppendItem(item)
}
} else {
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwFilter, newFilterTerm)
}
+23 -18
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operand-fraction.go // operand-fraction.go
@@ -8,27 +8,29 @@ package expr
import ( import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
// -------- fraction term // -------- fraction term
func newFractionTerm(tk *Token) *term { func newFractionTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Children: make([]*scan.Term, 0, 2),
children: make([]*term, 0, 2), Position: scan.PosInfix,
position: posInfix, Priority: scan.PriFraction,
priority: priFraction, EvalFunc: evalFraction,
evalFunc: evalFraction,
} }
} }
// -------- eval func // -------- eval func
func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) { func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var numValue, denValue any var numValue, denValue any
var num, den int64 var num, den int64
var ok bool var ok bool
if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil { if numValue, denValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
if num, ok = numValue.(int64); !ok { if num, ok = numValue.(int64); !ok {
@@ -40,7 +42,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
return return
} }
if den == 0 { if den == 0 {
err = opTerm.errDivisionByZero() err = opTerm.Errorf("division by zero")
return return
} }
@@ -49,16 +51,19 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
num = -num num = -num
} }
if num != 0 { if num != 0 {
g := gcd(num, den) if g := kern.Gcd(num, den); g != 1 {
num = num / g num = num / g
den = den / g den = den / g
}
if den == 1 { if den == 1 {
v = num v = num
} else { } else {
v = &FractionType{num, den} // v = &expr.FractionType{num, den}
v = kern.NewFraction(num, den)
} }
} else { } else {
v = &FractionType{0, den} // v = &FractionType{0, den}
v = kern.NewFraction(0, den)
} }
return return
} }
@@ -66,5 +71,5 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
// registerTermConstructor(SymVertBar, newFractionTerm) // registerTermConstructor(SymVertBar, newFractionTerm)
registerTermConstructor(SymColon, newFractionTerm) scan.RegisterTermConstructor(scan.SymColon, newFractionTerm)
} }
+109
View File
@@ -0,0 +1,109 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-groupby.go
package expr
import (
"fmt"
"io"
"strconv"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- group by term
func newGroupByTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalGroupBy,
}
}
func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
var sKey string
var keyByIndex bool
var ok bool
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if it, ok = leftValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
}
rightTk := opTerm.Children[1].Tk
if rightTk.IsSymbol(scan.SymVariable) && rightTk.Source() == "__" {
keyByIndex = true
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
return
} else if kern.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()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
var sItemKey string
if d, ok := item.(*kern.DictType); ok {
if keyByIndex || len(sKey) == 0 {
sItemKey = strconv.Itoa(int(it.Index()))
} else if d.HasKey(sKey) {
if keyValue, exists := d.GetItem(sKey); exists {
sItemKey = fmt.Sprintf("%v", keyValue)
} else {
sItemKey = "_"
}
} else {
sItemKey = "_"
}
} else {
sItemKey = strconv.Itoa(int(it.Index()))
}
var ls *kern.ListType
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
ls = lsAny.(*kern.ListType)
}
if ls == nil {
ls = kern.NewListA()
}
ls.AppendItem(item)
values.SetItem(sItemKey, ls)
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwGroupBy, newGroupByTerm)
}
+23 -18
View File
@@ -1,18 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-in.go // operator-in.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- in term //-------- in term
func newInTerm(tk *Token) (inst *term) { func newInTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalIn, EvalFunc: evalIn,
} }
} }
@@ -21,26 +26,26 @@ func newInTerm(tk *Token) (inst *term) {
// return // return
// } // }
func evalIn(ctx ExprContext, opTerm *term) (v any, err error) { func evalIn(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
if IsList(rightValue) { if kern.IsList(rightValue) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*kern.ListType)
v = list.indexDeepSameCmp(leftValue) >= 0 v = list.IndexDeepSameCmp(leftValue) >= 0
} else if IsDict(rightValue) { } else if kern.IsDict(rightValue) {
dict, _ := rightValue.(*DictType) dict, _ := rightValue.(*kern.DictType)
v = dict.hasKey(leftValue) v = dict.HasKey(leftValue)
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymKwIn, newInTerm) scan.RegisterTermConstructor(scan.SymKwIn, newInTerm)
} }
+20 -15
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-include.go // operator-include.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- include term //-------- include term
func newIncludeTerm(tk *Token) (inst *term) { func newIncludeTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalInclude, EvalFunc: evalInclude,
} }
} }
func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) { func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsList(childValue) { if kern.IsList(childValue) {
list, _ := childValue.(*ListType) list, _ := childValue.(*kern.ListType)
for i, filePathSpec := range *list { for i, filePathSpec := range *list {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
@@ -39,13 +44,13 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
break break
} }
} }
} else if IsString(childValue) { } else if kern.IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
} }
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
if err != nil { if err != nil {
//v = count //v = count
@@ -56,5 +61,5 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymKwInclude, newIncludeTerm) scan.RegisterTermConstructor(scan.SymKwInclude, newIncludeTerm)
} }
+35 -30
View File
@@ -1,29 +1,34 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-index.go // operator-index.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- index term // -------- index term
func newIndexTerm(tk *Token) (inst *term) { func newIndexTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priDot, Priority: scan.PriDot,
evalFunc: evalIndex, EvalFunc: evalIndex,
} }
} }
func verifyKey(indexList *ListType) (index any, err error) { func verifyKey(indexList *kern.ListType) (index any, err error) {
index = (*indexList)[0] index = (*indexList)[0]
return return
} }
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) { func verifyIndex(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (index int, err error) {
var v int var v int
if v, err = ToGoInt((*indexList)[0], "index expression"); err == nil { if v, err = kern.ToGoInt((*indexList)[0], "index expression"); err == nil {
if v < 0 && v >= -maxValue { if v < 0 && v >= -maxValue {
v = maxValue + v v = maxValue + v
} }
@@ -36,11 +41,11 @@ func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int,
return return
} }
func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex, endIndex int, err error) { func verifyRange(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (startIndex, endIndex int, err error) {
v, _ := ((*indexList)[0]).(*intPair) v, _ := ((*indexList)[0]).(*intPair)
startIndex = v.a startIndex = v.a
endIndex = v.b endIndex = v.b
if endIndex == ConstLastIndex { if endIndex == kern.ConstLastIndex {
endIndex = maxValue endIndex = maxValue
} }
if startIndex < 0 && startIndex >= -maxValue { if startIndex < 0 && startIndex >= -maxValue {
@@ -59,17 +64,17 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
return return
} }
func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) { func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var indexList *ListType var indexList *kern.ListType
var ok bool var ok bool
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
indexTerm := opTerm.children[1] indexTerm := opTerm.Children[1]
if indexList, ok = rightValue.(*ListType); !ok { if indexList, ok = rightValue.(*kern.ListType); !ok {
err = opTerm.Errorf("invalid index expression") err = opTerm.Errorf("invalid index expression")
return return
} else if len(*indexList) != 1 { } else if len(*indexList) != 1 {
@@ -77,9 +82,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
return return
} }
if IsInteger((*indexList)[0]) { if kern.IsInteger((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case *ListType: case *kern.ListType:
var index int var index int
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil { if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
v = (*unboxedValue)[index] v = (*unboxedValue)[index]
@@ -89,17 +94,17 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil { if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case *DictType: case *kern.DictType:
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue) v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default: default:
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
} else if isIntPair((*indexList)[0]) { } else if isIntPair((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case *ListType: case *kern.ListType:
var start, end int var start, end int
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil { if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
sublist := ListType((*unboxedValue)[start:end]) sublist := kern.ListType((*unboxedValue)[start:end])
v = &sublist v = &sublist
} }
case string: case string:
@@ -108,19 +113,19 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
v = unboxedValue[start:end] v = unboxedValue[start:end]
} }
default: default:
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
} else if IsDict(leftValue) { } else if kern.IsDict(leftValue) {
d := leftValue.(*DictType) d := leftValue.(*kern.DictType)
v, err = getDictItem(d, indexTerm, indexList, rightValue) v, err = getDictItem(d, indexTerm, indexList, rightValue)
} else { } else {
rightChild := opTerm.children[1] rightChild := opTerm.Children[1]
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0]) err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
} }
return return
} }
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) { func getDictItem(d *kern.DictType, indexTerm *scan.Term, indexList *kern.ListType, rightValue any) (v any, err error) {
var ok bool var ok bool
var indexValue any var indexValue any
@@ -134,5 +139,5 @@ func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue a
// init // init
func init() { func init() {
registerTermConstructor(SymIndex, newIndexTerm) scan.RegisterTermConstructor(scan.SymIndex, newIndexTerm)
} }
+37 -32
View File
@@ -1,67 +1,72 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-insert.go // operator-insert.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- prepend term //-------- prepend term
func newPrependTerm(tk *Token) (inst *term) { func newPrependTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priInsert, Priority: scan.PriInsert,
evalFunc: evalPrepend, EvalFunc: evalPrepend,
} }
} }
func newAppendTerm(tk *Token) (inst *term) { func newAppendTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priInsert, Priority: scan.PriInsert,
evalFunc: evalAppend, EvalFunc: evalAppend,
} }
} }
func evalPrepend(ctx ExprContext, opTerm *term) (v any, err error) { func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
if IsList(rightValue) { if kern.IsList(rightValue) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*kern.ListType)
newList := append(ListType{leftValue}, *list...) newList := append(kern.ListType{leftValue}, *list...)
v = &newList v = &newList
if opTerm.children[1].symbol() == SymVariable { if opTerm.Children[1].Symbol() == scan.SymVariable {
ctx.UnsafeSetVar(opTerm.children[1].source(), v) ctx.UnsafeSetVar(opTerm.Children[1].Source(), v)
} }
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) { func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
if IsList(leftValue) { if kern.IsList(leftValue) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*kern.ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
if opTerm.children[0].symbol() == SymVariable { if opTerm.Children[0].Symbol() == scan.SymVariable {
ctx.UnsafeSetVar(opTerm.children[0].source(), v) ctx.UnsafeSetVar(opTerm.Children[0].Source(), v)
} }
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -86,6 +91,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymPlusGreater, newPrependTerm) scan.RegisterTermConstructor(scan.SymPlusGreater, newPrependTerm)
registerTermConstructor(SymLessPlus, newAppendTerm) scan.RegisterTermConstructor(scan.SymLessPlus, newAppendTerm)
} }
+18 -13
View File
@@ -1,32 +1,37 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-iter-value.go // operator-iter-value.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- iter value term //-------- iter value term
func newIterValueTerm(tk *Token) (inst *term) { func newIterValueTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priDereference, Priority: scan.PriDereference,
evalFunc: evalIterValue, EvalFunc: evalIterValue,
} }
} }
func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) { func evalIterValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(kern.Iterator); ok {
v, err = it.Current() v, err = it.Current()
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
@@ -34,5 +39,5 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm) // registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymDereference, newIterValueTerm) scan.RegisterTermConstructor(scan.SymDereference, newIterValueTerm)
} }
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-join.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- join term
func newJoinTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalJoin,
}
}
func evalJoin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var itLeft, itRight kern.Iterator
var item any
var ok bool
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
return
}
if itLeft, ok = leftValue.(kern.Iterator); !ok {
if itLeft, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
}
if itRight, ok = rightValue.(kern.Iterator); !ok {
if itRight, err = NewIterator(ctx, rightValue, nil); err != nil {
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue))
}
}
values := kern.NewListA()
for _, it := range []kern.Iterator{itLeft, itRight} {
for item, err = it.Next(); err == nil; item, err = it.Next() {
values.AppendItem(item)
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwJoin, newJoinTerm)
}
+23 -18
View File
@@ -1,38 +1,43 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-length.go // operator-length.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- length term //-------- length term
func newLengthTerm(tk *Token) (inst *term) { func newLengthTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalLength, EvalFunc: evalLength,
} }
} }
func evalLength(ctx ExprContext, opTerm *term) (v any, err error) { func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if IsList(childValue) { if kern.IsList(childValue) {
ls, _ := childValue.(*ListType) ls, _ := childValue.(*kern.ListType)
v = int64(len(*ls)) v = int64(len(*ls))
} else if IsString(childValue) { } else if kern.IsString(childValue) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if IsDict(childValue) { } else if kern.IsDict(childValue) {
m, _ := childValue.(*DictType) m, _ := childValue.(*kern.DictType)
v = int64(len(*m)) v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(kern.Iterator); ok {
v = int64(it.Count()) v = int64(it.Count())
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) { // if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
// count, _ := extIt.CallOperation(CountName, nil) // count, _ := extIt.CallOperation(CountName, nil)
@@ -41,12 +46,12 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
// v = int64(it.Index() + 1) // v = int64(it.Index() + 1)
// } // }
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymHash, newLengthTerm) scan.RegisterTermConstructor(scan.SymHash, newLengthTerm)
} }
+31 -21
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-map.go // operator-map.go
@@ -7,45 +7,53 @@ package expr
import ( import (
"fmt" "fmt"
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
//-------- map term //-------- map term
func newMapTerm(tk *Token) (inst *term) { func newMapTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priIterOp, Priority: scan.PriIterOp,
evalFunc: evalMap, EvalFunc: evalMap,
} }
} }
func evalMap(ctx ExprContext, opTerm *term) (v any, err error) { func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var it Iterator var it kern.Iterator
var item any var item any
var ok bool
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
if leftValue, err = opTerm.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return return
} }
if it, err = NewIterator(leftValue); err != nil { if it, ok = leftValue.(kern.Iterator); !ok {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue)) if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
} }
values := newListA() values := kern.NewListA()
for item, err = it.Next(); err == nil; item, err = it.Next() { for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item) ctx.SetVar("_", item)
ctx.SetVar("_index", it.Index()) ctx.SetVar("__", it.Index())
ctx.SetVar("_count", it.Count()) ctx.SetVar("_#", it.Count())
if rightValue, err = opTerm.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
values.appendItem(rightValue) values.AppendItem(rightValue)
} }
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_") ctx.DeleteVar("_")
if err != nil { if err != nil {
break break
@@ -54,11 +62,13 @@ func evalMap(ctx ExprContext, opTerm *term) (v any, err error) {
if err == io.EOF { if err == io.EOF {
err = nil err = nil
} }
v = values if err == nil {
v = values
}
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymKwMap, newMapTerm) scan.RegisterTermConstructor(scan.SymKwMap, newMapTerm)
} }
+17 -12
View File
@@ -1,30 +1,35 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-plugin.go // operator-plugin.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- plugin term //-------- plugin term
func newPluginTerm(tk *Token) (inst *term) { func newPluginTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalPlugin, EvalFunc: evalPlugin,
} }
} }
func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) { func evalPlugin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
var count int var count int
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if count, err = importPluginFromSearchPath(childValue); err == nil { if count, err = importPluginFromSearchPath(ctx, childValue); err == nil {
v = int64(count) v = int64(count)
} }
return return
@@ -32,5 +37,5 @@ func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymKwPlugin, newPluginTerm) scan.RegisterTermConstructor(scan.SymKwPlugin, newPluginTerm)
} }
+39 -36
View File
@@ -1,93 +1,96 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-post-inc-dec.go // operator-post-inc-dec.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- post increment term // -------- post increment term
func newPostIncTerm(tk *Token) *term { func newPostIncTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Children: make([]*scan.Term, 0, 1),
children: make([]*term, 0, 1), Position: scan.PosPostfix,
position: posPostfix, Priority: scan.PriIncDec,
priority: priIncDec, EvalFunc: evalPostInc,
evalFunc: evalPostInc,
} }
} }
func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) { func evalPostInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(kern.Iterator); ok {
var namePrefix string var namePrefix string
v, err = it.Next() v, err = it.Next()
if opTerm.children[0].symbol() == SymVariable { if opTerm.Children[0].Symbol() == scan.SymVariable {
namePrefix = opTerm.children[0].source() namePrefix = opTerm.Children[0].Source()
} }
ctx.UnsafeSetVar(namePrefix+"_index", it.Index()) ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
if c, err1 := it.Current(); err1 == nil { if c, err1 := it.Current(); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_current", c) ctx.UnsafeSetVar(namePrefix+"_current", c)
} }
if it.HasOperation(KeyName) { if it.HasOperation(kern.KeyName) {
if k, err1 := it.CallOperation(KeyName, nil); err1 == nil { if k, err1 := it.CallOperation(kern.KeyName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_key", k) ctx.UnsafeSetVar(namePrefix+"_key", k)
} }
} }
if it.HasOperation(ValueName) { if it.HasOperation(kern.ValueName) {
if v1, err1 := it.CallOperation(ValueName, nil); err1 == nil { if v1, err1 := it.CallOperation(kern.ValueName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_value", v1) ctx.UnsafeSetVar(namePrefix+"_value", v1)
} }
} }
} else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable { } else if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i+1) ctx.SetVar(opTerm.Children[0].Source(), i+1)
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// -------- post decrement term // -------- post decrement term
func newPostDecTerm(tk *Token) *term { func newPostDecTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Children: make([]*scan.Term, 0, 1),
children: make([]*term, 0, 1), Position: scan.PosPostfix,
position: posPostfix, Priority: scan.PriIncDec,
priority: priIncDec, EvalFunc: evalPostDec,
evalFunc: evalPostDec,
} }
} }
func evalPostDec(ctx ExprContext, opTerm *term) (v any, err error) { func evalPostDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
/* if it, ok := childValue.(Iterator); ok { /* if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else */if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable { } else */if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].source(), i-1) ctx.SetVar(opTerm.Children[0].Source(), i-1)
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm) scan.RegisterTermConstructor(scan.SymDoublePlus, newPostIncTerm)
registerTermConstructor(SymDoubleMinus, newPostDecTerm) scan.RegisterTermConstructor(scan.SymDoubleMinus, newPostDecTerm)
} }
+33 -29
View File
@@ -1,69 +1,73 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-pre-inc-dec.go // operator-pre-inc-dec.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- pre increment term // -------- pre increment term
func newPreIncTerm(tk *Token) *term { func newPreIncTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Children: make([]*scan.Term, 0, 1),
children: make([]*term, 0, 1), Position: scan.PosPrefix,
position: posPrefix, Priority: scan.PriIncDec,
priority: priIncDec, EvalFunc: evalPreInc,
evalFunc: evalPreInc,
} }
} }
func evalPreInc(ctx ExprContext, opTerm *term) (v any, err error) { func evalPreInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable { if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
i := childValue.(int64) + 1 i := childValue.(int64) + 1
ctx.SetVar(opTerm.children[0].source(), i) ctx.SetVar(opTerm.Children[0].Source(), i)
v = i v = i
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// -------- pre decrement term // -------- pre decrement term
func newPreDecTerm(tk *Token) *term { func newPreDecTerm(tk *scan.Token) *scan.Term {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
parent: nil, Parent: nil,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priIncDec, Priority: scan.PriIncDec,
evalFunc: evalPreDec, EvalFunc: evalPreDec,
} }
} }
func evalPreDec(ctx ExprContext, opTerm *term) (v any, err error) { func evalPreDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable { if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
i := childValue.(int64) - 1 i := childValue.(int64) - 1
ctx.SetVar(opTerm.children[0].source(), i) ctx.SetVar(opTerm.Children[0].Source(), i)
v = i v = i
} else { } else {
err = opTerm.errIncompatibleType(childValue) err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymPreInc, newPreIncTerm) scan.RegisterTermConstructor(scan.SymPreInc, newPreIncTerm)
registerTermConstructor(SymPreDec, newPreDecTerm) scan.RegisterTermConstructor(scan.SymPreDec, newPreDecTerm)
} }
+76 -73
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-prod.go // operator-prod.go
@@ -6,45 +6,48 @@ package expr
import ( import (
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
//-------- multiply term //-------- multiply term
func newMultiplyTerm(tk *Token) (inst *term) { func newMultiplyTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priProduct, Priority: scan.PriProduct,
evalFunc: evalMultiply, EvalFunc: evalMultiply,
} }
} }
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) { func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if IsString(leftValue) && IsInteger(rightValue) { if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
s, _ := leftValue.(string) s, _ := leftValue.(string)
n, _ := rightValue.(int64) n, _ := rightValue.(int64)
v = strings.Repeat(s, int(n)) v = strings.Repeat(s, int(n))
} else if isNumOrFract(leftValue) && isNumOrFract(rightValue) { } else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) * numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) { } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
v, err = mulAnyFract(leftValue, rightValue) v, err = kern.MulAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt * rightInt v = leftInt * rightInt
} }
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) { func evalMultiply(ctx kern.ExprContext, prodTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = prodTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -53,44 +56,44 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
//-------- divide term //-------- divide term
func newDivideTerm(tk *Token) (inst *term) { func newDivideTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priProduct, Priority: scan.PriProduct,
evalFunc: evalDivide, EvalFunc: evalDivide,
} }
} }
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) { func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
d := numAsFloat(rightValue) d := kern.NumAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = opTerm.errDivisionByZero() err = opTerm.ErrDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = kern.NumAsFloat(leftValue) / d
} }
} else if isFraction(leftValue) || isFraction(rightValue) { } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
v, err = divAnyFract(leftValue, rightValue) v, err = kern.DivAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
if rightInt, _ := rightValue.(int64); rightInt == 0 { if rightInt, _ := rightValue.(int64); rightInt == 0 {
err = opTerm.errDivisionByZero() err = opTerm.ErrDivisionByZero()
} else { } else {
v = leftInt / rightInt v = leftInt / rightInt
} }
} }
} else if IsString(leftValue) && IsString(rightValue) { } else if kern.IsString(leftValue) && kern.IsString(rightValue) {
source := leftValue.(string) source := leftValue.(string)
sep := rightValue.(string) sep := rightValue.(string)
v = ListFromStrings(strings.Split(source, sep)) v = kern.ListFromStrings(strings.Split(source, sep))
} else if IsString(leftValue) && IsInteger(rightValue) { } else if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
source := leftValue.(string) source := leftValue.(string)
partSize := int(rightValue.(int64)) partSize := int(rightValue.(int64))
if partSize == 0 { if partSize == 0 {
err = opTerm.errDivisionByZero() err = opTerm.ErrDivisionByZero()
} else { } else {
partCount := len(source) / partSize partCount := len(source) / partSize
remainder := len(source) % partSize remainder := len(source) % partSize
@@ -105,18 +108,18 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
if remainder > 0 { if remainder > 0 {
parts = append(parts, source[len(source)-remainder:]) parts = append(parts, source[len(source)-remainder:])
} }
v = newList(parts) v = kern.NewList(parts)
} }
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) { func evalDivide(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -125,66 +128,66 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- divide as float term //-------- divide as float term
func newDivideAsFloatTerm(tk *Token) (inst *term) { func newDivideAsFloatTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priProduct, Priority: scan.PriProduct,
evalFunc: evalDivideAsFloat, EvalFunc: evalDivideAsFloat,
} }
} }
func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) { func evalDivideAsFloat(ctx kern.ExprContext, floatDivTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = floatDivTerm.EvalInfix(ctx); err != nil {
return return
} }
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
d := numAsFloat(rightValue) d := kern.NumAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = floatDivTerm.errDivisionByZero() err = floatDivTerm.ErrDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = kern.NumAsFloat(leftValue) / d
} }
} else { } else {
err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue) err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
//-------- reminder term //-------- reminder term
func newRemainderTerm(tk *Token) (inst *term) { func newRemainderTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priProduct, Priority: scan.PriProduct,
evalFunc: evalRemainder, EvalFunc: evalRemainder,
} }
} }
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) { func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) { if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
if rightInt == 0 { if rightInt == 0 {
err = opTerm.errDivisionByZero() err = opTerm.ErrDivisionByZero()
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
v = leftInt % rightInt v = leftInt % rightInt
} }
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) { func evalRemainder(ctx kern.ExprContext, remainderTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = remainderTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -193,8 +196,8 @@ func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymStar, newMultiplyTerm) scan.RegisterTermConstructor(scan.SymStar, newMultiplyTerm)
registerTermConstructor(SymSlash, newDivideTerm) scan.RegisterTermConstructor(scan.SymSlash, newDivideTerm)
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm) scan.RegisterTermConstructor(scan.SymDotSlash, newDivideAsFloatTerm)
registerTermConstructor(SymPercent, newRemainderTerm) scan.RegisterTermConstructor(scan.SymPercent, newRemainderTerm)
} }
+30 -25
View File
@@ -1,10 +1,15 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-range.go // operator-range.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
// -------- range term // -------- range term
type intPair struct { type intPair struct {
@@ -12,10 +17,10 @@ type intPair struct {
} }
func (p *intPair) TypeName() string { func (p *intPair) TypeName() string {
return TypePair return kern.TypePair
} }
func (p *intPair) ToString(opt FmtOpt) string { func (p *intPair) ToString(opt kern.FmtOpt) string {
return fmt.Sprintf("(%d, %d)", p.a, p.b) return fmt.Sprintf("(%d, %d)", p.a, p.b)
} }
@@ -24,38 +29,38 @@ func isIntPair(v any) bool {
return ok return ok
} }
func newRangeTerm(tk *Token) (inst *term) { func newRangeTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRange, Priority: scan.PriRange,
evalFunc: evalRange, EvalFunc: evalRange,
} }
} }
func changeColonToRange(t *term) { func changeColonToRange(t *scan.Term) {
if t.tk.IsSymbol(SymColon) { if t.Tk.IsSymbol(scan.SymColon) {
t.tk.Sym = SymRange t.Tk.Sym = scan.SymRange
t.evalFunc = evalRange t.EvalFunc = evalRange
} }
} }
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) { func evalRange(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if len(opTerm.children) == 0 { if len(opTerm.Children) == 0 {
leftValue = int64(0) leftValue = int64(0)
rightValue = int64(-1) rightValue = int64(-1)
} else if len(opTerm.children) == 1 { } else if len(opTerm.Children) == 1 {
if leftValue, err = opTerm.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return return
} }
rightValue = int64(ConstLastIndex) rightValue = int64(kern.ConstLastIndex)
} else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { } else if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
if !(IsInteger(leftValue) && IsInteger(rightValue)) { if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
// err = opTerm.errIncompatibleTypes(leftValue, rightValue) // err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = errRangeInvalidSpecification(opTerm) err = errRangeInvalidSpecification(opTerm)
return return
@@ -68,15 +73,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
return return
} }
func errRangeInvalidSpecification(t *term) error { func errRangeInvalidSpecification(t *scan.Term) error {
return t.Errorf("invalid range specification") return t.Errorf("invalid range specification")
} }
func errRangeUnexpectedExpression(t *term) error { func errRangeUnexpectedExpression(t *scan.Term) error {
return t.Errorf("unexpected range expression") return t.Errorf("unexpected range expression")
} }
// init // init
func init() { func init() {
registerTermConstructor(SymRange, newRangeTerm) scan.RegisterTermConstructor(scan.SymRange, newRangeTerm)
} }
+90 -85
View File
@@ -1,42 +1,47 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-rel.go // operator-rel.go
package expr package expr
import "reflect" import (
"reflect"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- equal term //-------- equal term
func newEqualTerm(tk *Token) (inst *term) { func newEqualTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalEqual, EvalFunc: evalEqual,
} }
} }
type deepFuncTemplate func(a, b any) (eq bool, err error) // type deepFuncTemplate func(a, b any) (eq bool, err error)
func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) { func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if kern.IsNumber(a) && kern.IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if kern.IsInteger(a) && kern.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
eq = li == ri eq = li == ri
} else { } else {
eq = numAsFloat(a) == numAsFloat(b) eq = kern.NumAsFloat(a) == kern.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = kern.CmpAnyFract(a, b); err == nil {
eq = cmp == 0 eq = cmp == 0
} }
} }
} else if deepCmp != nil && IsList(a) && IsList(b) { } else if deepCmp != nil && kern.IsList(a) && kern.IsList(b) {
eq, err = deepCmp(a, b) eq, err = deepCmp(a, b)
} else { } else {
eq = reflect.DeepEqual(a, b) eq = reflect.DeepEqual(a, b)
@@ -45,10 +50,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) { func evalEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -58,19 +63,19 @@ func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- not equal term //-------- not equal term
func newNotEqualTerm(tk *Token) (inst *term) { func newNotEqualTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalNotEqual, EvalFunc: evalNotEqual,
} }
} }
func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) { func evalNotEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
if v, err = evalEqual(ctx, opTerm); err == nil { if v, err = evalEqual(ctx, opTerm); err == nil {
b, _ := ToBool(v) b, _ := kern.ToBool(v)
v = !b v = !b
} }
return return
@@ -78,51 +83,51 @@ func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- less term //-------- less term
func newLessTerm(tk *Token) (inst *term) { func newLessTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalLess, EvalFunc: evalLess,
} }
} }
func lessThan(self *term, a, b any) (isLess bool, err error) { func lessThan(self *scan.Term, a, b any) (isLess bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if kern.IsNumber(a) && kern.IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if kern.IsInteger(a) && kern.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
isLess = li < ri isLess = li < ri
} else { } else {
isLess = numAsFloat(a) < numAsFloat(b) isLess = kern.NumAsFloat(a) < kern.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = kern.CmpAnyFract(a, b); err == nil {
isLess = cmp < 0 isLess = cmp < 0
} }
} }
} else if IsString(a) && IsString(b) { } else if kern.IsString(a) && kern.IsString(b) {
ls, _ := a.(string) ls, _ := a.(string)
rs, _ := b.(string) rs, _ := b.(string)
isLess = ls < rs isLess = ls < rs
// Inclusion test // Inclusion test
} else if IsList(a) && IsList(b) { } else if kern.IsList(a) && kern.IsList(b) {
aList, _ := a.(*ListType) aList, _ := a.(*kern.ListType)
bList, _ := b.(*ListType) bList, _ := b.(*kern.ListType)
isLess = len(*aList) < len(*bList) && bList.contains(aList) isLess = len(*aList) < len(*bList) && bList.Contains(aList)
} else { } else {
err = self.errIncompatibleTypes(a, b) err = self.ErrIncompatibleTypes(a, b)
} }
return return
} }
func evalLess(ctx ExprContext, opTerm *term) (v any, err error) { func evalLess(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(opTerm, leftValue, rightValue) v, err = lessThan(opTerm, leftValue, rightValue)
@@ -131,21 +136,21 @@ func evalLess(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- less or equal term //-------- less or equal term
func newLessEqualTerm(tk *Token) (inst *term) { func newLessEqualTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalLessEqual, EvalFunc: evalLessEqual,
} }
} }
func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) { func lessThanOrEqual(self *scan.Term, a, b any) (isLessEq bool, err error) {
if isLessEq, err = lessThan(self, a, b); err == nil { if isLessEq, err = lessThan(self, a, b); err == nil {
if !isLessEq { if !isLessEq {
if IsList(a) && IsList(b) { if kern.IsList(a) && kern.IsList(b) {
isLessEq, err = sameContent(a, b) isLessEq, err = kern.SameContent(a, b)
} else { } else {
isLessEq, err = equals(a, b, nil) isLessEq, err = equals(a, b, nil)
} }
@@ -154,10 +159,10 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
return return
} }
func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) { func evalLessEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -168,20 +173,20 @@ func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- greater term //-------- greater term
func newGreaterTerm(tk *Token) (inst *term) { func newGreaterTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalGreater, EvalFunc: evalGreater,
} }
} }
func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) { func evalGreater(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -191,20 +196,20 @@ func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) {
//-------- greater or equal term //-------- greater or equal term
func newGreaterEqualTerm(tk *Token) (inst *term) { func newGreaterEqualTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priRelational, Priority: scan.PriRelational,
evalFunc: evalGreaterEqual, EvalFunc: evalGreaterEqual,
} }
} }
func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) { func evalGreaterEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -214,10 +219,10 @@ func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleEqual, newEqualTerm) scan.RegisterTermConstructor(scan.SymDoubleEqual, newEqualTerm)
registerTermConstructor(SymNotEqual, newNotEqualTerm) scan.RegisterTermConstructor(scan.SymNotEqual, newNotEqualTerm)
registerTermConstructor(SymLess, newLessTerm) scan.RegisterTermConstructor(scan.SymLess, newLessTerm)
registerTermConstructor(SymLessOrEqual, newLessEqualTerm) scan.RegisterTermConstructor(scan.SymLessOrEqual, newLessEqualTerm)
registerTermConstructor(SymGreater, newGreaterTerm) scan.RegisterTermConstructor(scan.SymGreater, newGreaterTerm)
registerTermConstructor(SymGreaterOrEqual, newGreaterEqualTerm) scan.RegisterTermConstructor(scan.SymGreaterOrEqual, newGreaterEqualTerm)
} }
+25 -20
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-selector.go // operator-selector.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- selector term //-------- selector term
func newSelectorTerm(tk *Token) (inst *term) { func newSelectorTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 3), Children: make([]*scan.Term, 0, 3),
position: posInfix, Position: scan.PosInfix,
priority: priSelector, Priority: scan.PriSelector,
evalFunc: evalSelector, EvalFunc: evalSelector,
} }
} }
func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) { func trySelectorCase(ctx kern.ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
caseData, _ := caseSel.(*selectorCase) caseData, _ := caseSel.(*selectorCase)
if caseData.filterList == nil { if caseData.filterList == nil {
selectedValue, err = caseData.caseExpr.Eval(ctx) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
} else if filterList, ok := caseData.filterList.value().([]*term); ok { } else if filterList, ok := caseData.filterList.Value().([]*scan.Term); ok {
if len(filterList) == 0 { if len(filterList) == 0 {
var valueAsInt = int64(0) var valueAsInt = int64(0)
if b, ok := exprValue.(bool); ok { if b, ok := exprValue.(bool); ok {
@@ -38,7 +43,7 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
} else { } else {
var caseValue any var caseValue any
for _, caseTerm := range filterList { for _, caseTerm := range filterList {
if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue { if caseValue, err = caseTerm.Compute(ctx); err != nil || caseValue == exprValue {
selectedValue, err = caseData.caseExpr.Eval(ctx) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
break break
@@ -49,33 +54,33 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
return return
} }
func evalSelector(ctx ExprContext, opTerm *term) (v any, err error) { func evalSelector(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var exprValue any var exprValue any
var match bool var match bool
if err = opTerm.checkOperands(); err != nil { if err = opTerm.CheckOperands(); err != nil {
return return
} }
exprTerm := opTerm.children[0] exprTerm := opTerm.Children[0]
if exprValue, err = exprTerm.compute(ctx); err != nil { if exprValue, err = exprTerm.Compute(ctx); err != nil {
return return
} }
caseListTerm := opTerm.children[1] caseListTerm := opTerm.Children[1]
caseList, _ := caseListTerm.value().([]*term) caseList, _ := caseListTerm.Value().([]*scan.Term)
for i, caseTerm := range caseList { for i, caseTerm := range caseList {
caseSel := caseTerm.value() caseSel := caseTerm.Value()
if match, v, err = trySelectorCase(ctx, exprValue, caseSel, i); err != nil || match { if match, v, err = trySelectorCase(ctx, exprValue, caseSel, i); err != nil || match {
break break
} }
} }
if err == nil && !match { if err == nil && !match {
err = exprTerm.tk.Errorf("no case catches the value (%v) of the selection expression", exprValue) err = exprTerm.Tk.Errorf("no case catches the value (%v) of the selection expression", exprValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymSelector, newSelectorTerm) scan.RegisterTermConstructor(scan.SymSelector, newSelectorTerm)
} }
+32 -27
View File
@@ -1,36 +1,41 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-shift.go // operator-shift.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- bit right shift term //-------- bit right shift term
func newRightShiftTerm(tk *Token) (inst *term) { func newRightShiftTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBinShift, Priority: scan.PriBinShift,
evalFunc: evalRightShift, EvalFunc: evalRightShift,
} }
} }
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) { func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) { if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
leftInt := leftValue.(int64) leftInt := leftValue.(int64)
rightInt := rightValue.(int64) rightInt := rightValue.(int64)
v = leftInt >> rightInt v = leftInt >> rightInt
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) { func evalRightShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -38,31 +43,31 @@ func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
return return
} }
func newLeftShiftTerm(tk *Token) (inst *term) { func newLeftShiftTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priBinShift, Priority: scan.PriBinShift,
evalFunc: evalLeftShift, EvalFunc: evalLeftShift,
} }
} }
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) { func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if IsInteger(leftValue) && IsInteger(rightValue) { if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
leftInt := leftValue.(int64) leftInt := leftValue.(int64)
rightInt := rightValue.(int64) rightInt := rightValue.(int64)
v = leftInt << rightInt v = leftInt << rightInt
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) { func evalLeftShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -72,6 +77,6 @@ func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleGreater, newRightShiftTerm) scan.RegisterTermConstructor(scan.SymDoubleGreater, newRightShiftTerm)
registerTermConstructor(SymDoubleLess, newLeftShiftTerm) scan.RegisterTermConstructor(scan.SymDoubleLess, newLeftShiftTerm)
} }
+29 -26
View File
@@ -1,62 +1,65 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-sign.go // operator-sign.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- plus sign term //-------- plus sign term
func newPlusSignTerm(tk *Token) (inst *term) { func newPlusSignTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
// class: classOperator, Children: make([]*scan.Term, 0, 1),
// kind: kindUnknown, Position: scan.PosPrefix,
children: make([]*term, 0, 1), Priority: scan.PriSign,
position: posPrefix, EvalFunc: evalSign,
priority: priSign,
evalFunc: evalSign,
} }
} }
func newMinusSignTerm(tk *Token) (inst *term) { func newMinusSignTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalSign, EvalFunc: evalSign,
} }
} }
func evalSign(ctx ExprContext, opTerm *term) (v any, err error) { func evalSign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = opTerm.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
if IsFloat(rightValue) { if kern.IsFloat(rightValue) {
if opTerm.tk.Sym == SymChangeSign { if opTerm.Tk.Sym == scan.SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if IsInteger(rightValue) { } else if kern.IsInteger(rightValue) {
if opTerm.tk.Sym == SymChangeSign { if opTerm.Tk.Sym == scan.SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
} else { } else {
v = rightValue v = rightValue
} }
} else { } else {
err = opTerm.errIncompatibleType(rightValue) err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymUnchangeSign, newPlusSignTerm) scan.RegisterTermConstructor(scan.SymUnchangeSign, newPlusSignTerm)
registerTermConstructor(SymChangeSign, newMinusSignTerm) scan.RegisterTermConstructor(scan.SymChangeSign, newMinusSignTerm)
} }
+57 -54
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-sum.go // operator-sum.go
@@ -7,64 +7,67 @@ package expr
import ( import (
"fmt" "fmt"
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
) )
//-------- plus term //-------- plus term
func newPlusTerm(tk *Token) (inst *term) { func newPlusTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priSum, Priority: scan.PriSum,
evalFunc: evalPlus, EvalFunc: evalPlus,
} }
} }
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) { func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) { if (kern.IsString(leftValue) && kern.IsNumberString(rightValue)) || (kern.IsString(rightValue) && kern.IsNumberString(leftValue)) {
v = fmt.Sprintf("%v%v", leftValue, rightValue) v = fmt.Sprintf("%v%v", leftValue, rightValue)
} else if IsNumber(leftValue) && IsNumber(rightValue) { } else if kern.IsNumber(leftValue) && kern.IsNumber(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt + rightInt v = leftInt + rightInt
} }
} else if IsList(leftValue) && IsList(rightValue) { } else if kern.IsList(leftValue) && kern.IsList(rightValue) {
var leftList, rightList *ListType var leftList, rightList *kern.ListType
leftList, _ = leftValue.(*ListType) leftList, _ = leftValue.(*kern.ListType)
rightList, _ = rightValue.(*ListType) rightList, _ = rightValue.(*kern.ListType)
sumList := make(ListType, 0, len(*leftList)+len(*rightList)) sumList := make(kern.ListType, 0, len(*leftList)+len(*rightList))
sumList = append(sumList, *leftList...) sumList = append(sumList, *leftList...)
sumList = append(sumList, *rightList...) sumList = append(sumList, *rightList...)
v = &sumList v = &sumList
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) { } else if (kern.IsFraction(leftValue) && kern.IsNumber(rightValue)) || (kern.IsFraction(rightValue) && kern.IsNumber(leftValue)) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
} else { } else {
v, err = sumAnyFract(leftValue, rightValue) v, err = kern.SumAnyFract(leftValue, rightValue)
} }
} else if IsDict(leftValue) && IsDict(rightValue) { } else if kern.IsDict(leftValue) && kern.IsDict(rightValue) {
leftDict, _ := leftValue.(*DictType) leftDict, _ := leftValue.(*kern.DictType)
rightDict, _ := rightValue.(*DictType) rightDict, _ := rightValue.(*kern.DictType)
c := leftDict.clone() c := leftDict.Clone()
c.merge(rightDict) c.Merge(rightDict)
v = c v = c
} else if isFraction(leftValue) && isFraction(rightValue) { } else if kern.IsFraction(leftValue) && kern.IsFraction(rightValue) {
v, err = sumAnyFract(leftValue, rightValue) v, err = kern.SumAnyFract(leftValue, rightValue)
} else { } else {
err = plusTerm.errIncompatibleTypes(leftValue, rightValue) err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return v, err return v, err
} }
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) { func evalPlus(ctx kern.ExprContext, plusTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = plusTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -73,31 +76,31 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
//-------- minus term //-------- minus term
func newMinusTerm(tk *Token) (inst *term) { func newMinusTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 2), Children: make([]*scan.Term, 0, 2),
position: posInfix, Position: scan.PosInfix,
priority: priSum, Priority: scan.PriSum,
evalFunc: evalMinus, EvalFunc: evalMinus,
} }
} }
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) { func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) - numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) - kern.NumAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) { } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
v, err = subAnyFract(leftValue, rightValue) v, err = kern.SubAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt - rightInt v = leftInt - rightInt
} }
} else if IsList(leftValue) && IsList(rightValue) { } else if kern.IsList(leftValue) && kern.IsList(rightValue) {
leftList, _ := leftValue.(*ListType) leftList, _ := leftValue.(*kern.ListType)
rightList, _ := rightValue.(*ListType) rightList, _ := rightValue.(*kern.ListType)
diffList := make(ListType, 0, len(*leftList)-len(*rightList)) diffList := make(kern.ListType, 0, len(*leftList)-len(*rightList))
for _, item := range *leftList { for _, item := range *leftList {
if slices.Index(*rightList, item) < 0 { if slices.Index(*rightList, item) < 0 {
diffList = append(diffList, item) diffList = append(diffList, item)
@@ -105,15 +108,15 @@ func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
} }
v = &diffList v = &diffList
} else { } else {
err = minusTerm.errIncompatibleTypes(leftValue, rightValue) err = minusTerm.ErrIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) { func evalMinus(ctx kern.ExprContext, minusTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = minusTerm.EvalInfix(ctx); err != nil {
return return
} }
@@ -122,6 +125,6 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymPlus, newPlusTerm) scan.RegisterTermConstructor(scan.SymPlus, newPlusTerm)
registerTermConstructor(SymMinus, newMinusTerm) scan.RegisterTermConstructor(scan.SymMinus, newMinusTerm)
} }
+21 -16
View File
@@ -1,24 +1,29 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// operator-unset.go // operator-unset.go
package expr package expr
import "strings" import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- unset term //-------- unset term
func newUnsetTerm(tk *Token) (inst *term) { func newUnsetTerm(tk *scan.Token) (inst *scan.Term) {
return &term{ return &scan.Term{
tk: *tk, Tk: *tk,
children: make([]*term, 0, 1), Children: make([]*scan.Term, 0, 1),
position: posPrefix, Position: scan.PosPrefix,
priority: priSign, Priority: scan.PriSign,
evalFunc: evalUnset, EvalFunc: evalUnset,
} }
} }
func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, err error) { func deleteContextItem(ctx kern.ExprContext, opTerm *scan.Term, item any) (deleted bool, err error) {
if name, ok := item.(string); ok { if name, ok := item.(string); ok {
var size int var size int
if strings.HasSuffix(name, "()") { if strings.HasSuffix(name, "()") {
@@ -31,22 +36,22 @@ func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, e
deleted = ctx.VarCount() < size deleted = ctx.VarCount() < size
} }
} else { } else {
err = opTerm.errIncompatibleType(item) err = opTerm.ErrIncompatiblePrefixPostfixType(item)
} }
return return
} }
func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) { func evalUnset(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var childValue any var childValue any
var deleted bool var deleted bool
if childValue, err = opTerm.evalPrefix(ctx); err != nil { if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsList(childValue) { if kern.IsList(childValue) {
list, _ := childValue.(*ListType) list, _ := childValue.(*kern.ListType)
for _, item := range *list { for _, item := range *list {
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil { if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
break break
@@ -65,5 +70,5 @@ func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymKwUnset, newUnsetTerm) scan.RegisterTermConstructor(scan.SymKwUnset, newUnsetTerm)
} }
+188 -186
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// parser.go // parser.go
@@ -7,6 +7,7 @@ package expr
import ( import (
"errors" "errors"
"git.portale-stac.it/go-pkg/expr/scan"
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
) )
@@ -54,34 +55,34 @@ func NewParser() (p *parser) {
return p return p
} }
func (parser *parser) Next(scanner *scanner) (tk *Token) { func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() { for tk = scanner.Next(); tk.IsSymbol(scan.SymComment); tk = scanner.Next() {
} }
return return
} }
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) { func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) {
args := make([]*term, 0, 10) args := make([]*scan.Term, 0, 10)
itemExpected := false itemExpected := false
lastSym := SymUnknown lastSym := scan.SymUnknown
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
var subTree *ast var subTree *scan.Ast
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil { if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err != nil {
break break
} }
prev := scanner.Previous() prev := scanner.Previous()
if subTree.root != nil { if subTree.Root() != nil {
args = append(args, subTree.root) args = append(args, subTree.Root())
} else if itemExpected { } else if itemExpected {
err = prev.ErrorExpectedGot("function-param-value") err = prev.ErrorExpectedGot("function-param-value")
break break
} }
itemExpected = prev.Sym == SymComma itemExpected = prev.Sym == scan.SymComma
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
} }
if err == nil { if err == nil {
if lastSym != SymClosedRound { if lastSym != scan.SymClosedRound {
err = errors.New("unterminated arguments list") err = errors.New("unterminated arguments list")
} else { } else {
tree = newFuncCallTerm(tk, args) tree = newFuncCallTerm(tk, args)
@@ -90,33 +91,33 @@ func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Tok
return return
} }
func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err error) {
// Example: "add = func(x,y) {x+y} // Example: "add = func(x,y) {x+y}
var body *ast var body *scan.Ast
args := make([]*term, 0) args := make([]*scan.Term, 0)
lastSym := SymUnknown lastSym := scan.SymUnknown
defaultParamsStarted := false defaultParamsStarted := false
itemExpected := false itemExpected := false
tk := scanner.Previous() tk := scanner.Previous()
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) { if tk.IsSymbol(scan.SymIdentifier) {
param := newTerm(tk) param := scan.NewTerm(tk)
if len(args) > 0 { if len(args) > 0 {
if pos := paramAlreadyDefined(args, param); pos > 0 { if pos := paramAlreadyDefined(args, param); pos > 0 {
err = tk.Errorf("parameter %q at position %d already defined at position %d", param.source(), len(args)+1, pos) err = tk.Errorf("parameter %q at position %d already defined at position %d", param.Source(), len(args)+1, pos)
break break
} }
} }
args = append(args, param) args = append(args, param)
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.Sym == SymEqual { if tk.Sym == scan.SymEqual {
var paramExpr *ast var paramExpr *scan.Ast
defaultParamsStarted = true defaultParamsStarted = true
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil { if paramExpr, err = parser.parseItem(scanner, parserNoFlags, scan.SymComma, scan.SymClosedRound); err != nil {
break break
} }
param.forceChild(paramExpr.root) param.ForceChild(paramExpr.Root())
} else if defaultParamsStarted { } else if defaultParamsStarted {
err = tk.Errorf("can't mix default and non-default parameters") err = tk.Errorf("can't mix default and non-default parameters")
break break
@@ -127,74 +128,74 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == SymComma itemExpected = lastSym == scan.SymComma
} }
if err == nil && lastSym != SymClosedRound { if err == nil && lastSym != scan.SymClosedRound {
err = tk.ErrorExpectedGot(")") err = tk.ErrorExpectedGot(")")
} }
if err == nil { if err == nil {
tk = parser.Next(scanner) tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) { if tk.IsSymbol(scan.SymOpenBrace) {
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace) body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymClosedBrace)
} else { } else {
err = tk.ErrorExpectedGot("{") err = tk.ErrorExpectedGot("{")
} }
} }
if err == nil { if err == nil {
if scanner.Previous().Sym != SymClosedBrace { if scanner.Previous().Sym != scan.SymClosedBrace {
err = scanner.Previous().ErrorExpectedGot("}") err = scanner.Previous().ErrorExpectedGot("}")
} else { } else {
tk = scanner.makeValueToken(SymExpression, "", body) tk = scanner.MakeValueToken(scan.SymExpression, "", body)
tree = newFuncDefTerm(tk, args) tree = newFuncDefTerm(tk, args)
} }
} }
return return
} }
func paramAlreadyDefined(args []*term, param *term) (position int) { func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
position = 0 position = 0
for i, arg := range args { for i, arg := range args {
if arg.source() == param.source() { if arg.Source() == param.Source() {
position = i + 1 position = i + 1
} }
} }
return return
} }
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) { func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
r, c := scanner.lastPos() r, c := scanner.LastPos()
args := make([]*term, 0) args := make([]*scan.Term, 0)
lastSym := SymUnknown lastSym := scan.SymUnknown
itemExpected := false itemExpected := false
itemCtx := remFlags(ctx, allowIndex) itemCtx := remFlags(ctx, allowIndex)
for lastSym != SymClosedSquare && lastSym != SymEos { for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
zeroRequired := scanner.current.Sym == SymColon zeroRequired := scanner.Current().Sym == scan.SymColon
var itemTree *ast var itemTree *scan.Ast
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil { if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
root := itemTree.root root := itemTree.Root()
if root != nil { if root != nil {
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon { if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
changeColonToRange(root) changeColonToRange(root)
} }
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange { if !hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange {
// err = root.Errorf("unexpected range expression") // err = root.Errorf("unexpected range expression")
err = errRangeUnexpectedExpression(root) err = errRangeUnexpectedExpression(root)
break break
} }
args = append(args, root) args = append(args, root)
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 { if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange && zeroRequired { //len(root.children) == 0 {
if len(root.children) == 1 { if len(root.Children) == 1 {
root.children = append(root.children, root.children[0]) root.Children = append(root.Children, root.Children[0])
} else if len(root.children) > 1 { } else if len(root.Children) > 1 {
// err = root.Errorf("invalid range specification") // err = root.Errorf("invalid range specification")
err = errRangeInvalidSpecification(root) err = errRangeInvalidSpecification(root)
break break
} }
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0)) zeroTk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymInteger, "0", int64(0))
zeroTerm := newTerm(zeroTk) zeroTerm := scan.NewTerm(zeroTk)
zeroTerm.setParent(root) zeroTerm.SetParent(root)
root.children[0] = zeroTerm root.Children[0] = zeroTerm
} }
} else if itemExpected { } else if itemExpected {
prev := scanner.Previous() prev := scanner.Previous()
@@ -205,12 +206,12 @@ func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
if itemExpected = lastSym == SymComma; itemExpected { if itemExpected = lastSym == scan.SymComma; itemExpected {
remFlags(ctx, allowIndex) remFlags(ctx, allowIndex)
} }
} }
if err == nil { if err == nil {
if lastSym != SymClosedSquare { if lastSym != scan.SymClosedSquare {
err = scanner.Previous().ErrorExpectedGot("]") err = scanner.Previous().ErrorExpectedGot("]")
} else { } else {
listTerm = newListTerm(r, c, args) listTerm = newListTerm(r, c, args)
@@ -219,16 +220,16 @@ func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *
return return
} }
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) { func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
tk := scanner.Previous() tk := scanner.Previous()
args := make([]*term, 0) args := make([]*scan.Term, 0)
lastSym := SymUnknown lastSym := scan.SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
var subTree *ast var subTree *scan.Ast
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil { if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err == nil {
if subTree.root != nil { if subTree.Root() != nil {
args = append(args, subTree.root) args = append(args, subTree.Root())
} else if itemExpected { } else if itemExpected {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("iterator-param") err = prev.ErrorExpectedGot("iterator-param")
@@ -238,10 +239,10 @@ func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == SymComma itemExpected = lastSym == scan.SymComma
} }
if err == nil { if err == nil {
if lastSym != SymClosedRound { if lastSym != scan.SymClosedRound {
err = scanner.Previous().ErrorExpectedGot(")") err = scanner.Previous().ErrorExpectedGot(")")
} else { } else {
subtree = newIteratorTerm(tk, args) subtree = newIteratorTerm(tk, args)
@@ -250,18 +251,18 @@ func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree
return return
} }
func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) { func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
tk := parser.Next(scanner) tk := parser.Next(scanner)
if tk.Sym == SymError { if tk.Sym == scan.SymError {
err = tk.Error() err = tk.Error()
return return
} }
if tk.Sym == SymClosedBrace || tk.Sym == SymEos { if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
return return
} }
if tk.Sym == SymInteger || tk.Sym == SymString { if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
tkSep := parser.Next(scanner) tkSep := parser.Next(scanner)
if tkSep.Sym != SymColon { if tkSep.Sym != scan.SymColon {
err = tkSep.ErrorExpectedGot(":") err = tkSep.ErrorExpectedGot(":")
} else { } else {
key = tk.Value key = tk.Value
@@ -272,12 +273,12 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
return return
} }
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) { func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
args := make(map[any]*term, 0) args := make(map[any]*scan.Term, 0)
lastSym := SymUnknown lastSym := scan.SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedBrace && lastSym != SymEos { for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
var subTree *ast var subTree *scan.Ast
var key any var key any
if key, err = parser.parseDictKey(scanner); err != nil { if key, err = parser.parseDictKey(scanner); err != nil {
break break
@@ -289,9 +290,9 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
} }
break break
} }
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil { if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
if subTree.root != nil { if subTree.Root() != nil {
args[key] = subTree.root args[key] = subTree.Root()
} else /*if key != nil*/ { } else /*if key != nil*/ {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("dictionary-value") err = prev.ErrorExpectedGot("dictionary-value")
@@ -301,10 +302,10 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
itemExpected = lastSym == SymComma itemExpected = lastSym == scan.SymComma
} }
if err == nil { if err == nil {
if lastSym != SymClosedBrace { if lastSym != scan.SymClosedBrace {
err = scanner.Previous().ErrorExpectedGot("}") err = scanner.Previous().ErrorExpectedGot("}")
} else { } else {
subtree = newDictTerm(args) subtree = newDictTerm(args)
@@ -313,14 +314,14 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
return return
} }
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) { func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext, defaultCase bool) (caseTerm *scan.Term, err error) {
var filterList *term var filterList *scan.Term
var caseExpr *ast var caseExpr *scan.Ast
ctx = remFlags(ctx, allowIndex) ctx = remFlags(ctx, allowIndex)
tk := parser.Next(scanner) tk := parser.Next(scanner)
startRow := tk.row startRow := tk.Row()
startCol := tk.col startCol := tk.Col()
if tk.Sym == SymOpenSquare { if tk.Sym == scan.SymOpenSquare {
if defaultCase { if defaultCase {
err = tk.Errorf("case list in default clause") err = tk.Errorf("case list in default clause")
return return
@@ -329,14 +330,14 @@ func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, def
return return
} }
tk = parser.Next(scanner) tk = parser.Next(scanner)
startRow = tk.row startRow = tk.Row()
startCol = tk.col startCol = tk.Col()
} else if !defaultCase { } else if !defaultCase {
filterList = newListTerm(startRow, startCol, make([]*term, 0)) filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0))
} }
if tk.Sym == SymOpenBrace { if tk.Sym == scan.SymOpenBrace {
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil { if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, scan.SymClosedBrace); err != nil {
return return
} }
} else { } else {
@@ -349,25 +350,25 @@ func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, def
return return
} }
func addSelectorCase(selectorTerm, caseTerm *term) { func addSelectorCase(selectorTerm, caseTerm *scan.Term) {
if len(selectorTerm.children) < 2 { if len(selectorTerm.Children) < 2 {
caseListTerm := newListTermA(caseTerm) caseListTerm := newListTermA(caseTerm)
selectorTerm.children = append(selectorTerm.children, caseListTerm) selectorTerm.Children = append(selectorTerm.Children, caseListTerm)
} else { } else {
caseListTerm := selectorTerm.children[1] caseListTerm := selectorTerm.Children[1]
caseList, _ := caseListTerm.value().([]*term) caseList, _ := caseListTerm.Value().([]*scan.Term)
caseList = append(caseList, caseTerm) caseList = append(caseList, caseTerm)
caseListTerm.tk.Value = caseList caseListTerm.Tk.Value = caseList
} }
caseTerm.parent = selectorTerm caseTerm.Parent = selectorTerm
} }
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) { func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx parserContext) (selectorTerm *scan.Term, err error) {
var caseTerm *term var caseTerm *scan.Term
ctx = remFlags(ctx, allowIndex) ctx = remFlags(ctx, allowIndex)
tk := scanner.makeToken(SymSelector, '?') tk := scanner.MakeToken(scan.SymSelector, '?')
if selectorTerm, err = tree.addToken(tk); err != nil { if selectorTerm, err = tree.AddToken(tk); err != nil {
return return
} }
@@ -377,61 +378,70 @@ func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserConte
return return
} }
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseItem(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...) return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
} }
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
termSymbols = append(termSymbols, SymEos) defer func() {
if r := recover(); r != nil {
if errVal, ok := r.(error); ok {
err = errVal
} else {
err = errors.New("unexpected error while parsing the expression")
}
}
}()
termSymbols = append(termSymbols, scan.SymEos)
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...) return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
} }
func couldBeACollection(t *term) bool { func couldBeACollection(t *scan.Term) bool {
var sym = SymUnknown var sym = scan.SymUnknown
if t != nil { if t != nil {
sym = t.symbol() sym = t.Symbol()
} }
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
} }
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) { func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
var tk *Token var tk *scan.Token
if allowIndeces { if allowIndeces {
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source()) tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), scan.SymIndex, listTerm.Source())
root = newTerm(tk) root = scan.NewTerm(tk)
if err = tree.addTerm(root); err == nil { if err = tree.AddTerm(root); err == nil {
err = tree.addTerm(listTerm) err = tree.AddTerm(listTerm)
} }
} else { } else {
root = listTerm root = listTerm
err = tree.addTerm(listTerm) err = tree.AddTerm(listTerm)
} }
return return
} }
func changePrefix(tk *Token) { func changePrefix(tk *scan.Token) {
switch tk.Sym { switch tk.Sym {
case SymMinus: case scan.SymMinus:
tk.SetSymbol(SymChangeSign) tk.SetSymbol(scan.SymChangeSign)
case SymPlus: case scan.SymPlus:
tk.SetSymbol(SymUnchangeSign) tk.SetSymbol(scan.SymUnchangeSign)
case SymStar: case scan.SymStar:
tk.SetSymbol(SymDereference) tk.SetSymbol(scan.SymDereference)
case SymExclamation: case scan.SymExclamation:
tk.SetSymbol(SymNot) tk.SetSymbol(scan.SymNot)
case SymDoublePlus: case scan.SymDoublePlus:
tk.SetSymbol(SymPreInc) tk.SetSymbol(scan.SymPreInc)
case SymDoubleMinus: case scan.SymDoubleMinus:
tk.SetSymbol(SymPreDec) tk.SetSymbol(scan.SymPreDec)
} }
} }
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
var selectorTerm *term = nil var selectorTerm *scan.Term = nil
var currentTerm *term = nil var currentTerm *scan.Term = nil
var tk *Token var tk *scan.Token
tree = NewAst() tree = scan.NewAst()
firstToken := true firstToken := true
// lastSym := SymUnknown // lastSym := SymUnknown
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) { for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
@@ -439,7 +449,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
// continue // continue
// } // }
if tk.Sym == SymSemiColon { if tk.Sym == scan.SymSemiColon {
if hasFlag(ctx, allowMultiExpr) { if hasFlag(ctx, allowMultiExpr) {
tree.ToForest() tree.ToForest()
firstToken = true firstToken = true
@@ -447,7 +457,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
selectorTerm = nil selectorTerm = nil
continue continue
} else { } else {
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source) err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
break break
} }
} }
@@ -455,99 +465,91 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
//fmt.Println("Token:", tk) //fmt.Println("Token:", tk)
if firstToken { if firstToken {
changePrefix(tk) changePrefix(tk)
// if tk.Sym == SymMinus {
// tk.Sym = SymChangeSign
// } else if tk.Sym == SymPlus {
// tk.Sym = SymUnchangeSign
// } else if tk.IsSymbol(SymStar) {
// tk.SetSymbol(SymDereference)
// } else if tk.IsSymbol(SymExclamation) {
// tk.SetSymbol(SymNot)
// }
firstToken = false firstToken = false
} }
switch tk.Sym { switch tk.Sym {
case SymOpenRound: case scan.SymOpenRound:
var subTree *ast var subTree *scan.Ast
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil { if subTree, err = parser.parseGeneral(scanner, ctx, scan.SymClosedRound); err == nil {
exprTerm := newExprTerm(subTree.root) if subTree.Root() == nil {
err = tree.addTerm(exprTerm) err = tk.ErrorExpectedGotString("expression", "()")
currentTerm = exprTerm } else {
// subTree.root.priority = priValue exprTerm := newExprTerm(subTree.Root())
// err = tree.addTerm(newExprTerm(subTree.root)) err = tree.AddTerm(exprTerm)
// currentTerm = subTree.root currentTerm = exprTerm
}
} }
case SymFuncCall: case scan.SymFuncCall:
var funcCallTerm *term var funcCallTerm *scan.Term
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil { if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
err = tree.addTerm(funcCallTerm) err = tree.AddTerm(funcCallTerm)
currentTerm = funcCallTerm currentTerm = funcCallTerm
} }
case SymOpenSquare: case scan.SymOpenSquare:
var listTerm *term var listTerm *scan.Term
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm)) newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
if listTerm, err = parser.parseList(scanner, newCtx); err == nil { if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex)) currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
} }
case SymOpenBrace: case scan.SymOpenBrace:
if currentTerm != nil && currentTerm.symbol() == SymColon { if currentTerm != nil && currentTerm.Symbol() == scan.SymColon {
err = currentTerm.Errorf(`selector-case outside of a selector context`) err = currentTerm.Errorf(`selector-case outside of a selector context`)
} else { } else {
var mapTerm *term var mapTerm *scan.Term
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil { if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
err = tree.addTerm(mapTerm) err = tree.AddTerm(mapTerm)
currentTerm = mapTerm currentTerm = mapTerm
} }
} }
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual: case scan.SymEqual, scan.SymPlusEqual, scan.SymMinusEqual, scan.SymStarEqual, scan.SymSlashEqual, scan.SymPercEqual, scan.SymAmpersandEqual, scan.SymVertBarEqual, scan.SymDoubleLessEqual, scan.SymDoubleGreaterEqual, scan.SymCaretEqual:
currentTerm, err = tree.addToken(tk) currentTerm, err = tree.AddToken(tk)
firstToken = true firstToken = true
case SymFuncDef: case scan.SymFuncDef:
var funcDefTerm *term var funcDefTerm *scan.Term
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil { if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
err = tree.addTerm(funcDefTerm) err = tree.AddTerm(funcDefTerm)
currentTerm = funcDefTerm currentTerm = funcDefTerm
} }
case SymDollarRound: case scan.SymDollarRound:
var iterDefTerm *term var iterDefTerm *scan.Term
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil { if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
err = tree.addTerm(iterDefTerm) err = tree.AddTerm(iterDefTerm)
currentTerm = iterDefTerm currentTerm = iterDefTerm
} }
case SymIdentifier: case scan.SymIdentifier:
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) { if tk.Source()[0] == '@' && !hasFlag(ctx, allowVarRef) {
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source) err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.Source())
} else { } else {
currentTerm, err = tree.addToken(tk) currentTerm, err = tree.AddToken(tk)
} }
case SymQuestion: case scan.SymQuestion:
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil { if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
currentTerm = selectorTerm currentTerm = selectorTerm
addFlags(ctx, selectorContext) addFlags(ctx, selectorContext)
} }
case SymColon, SymDoubleColon: case scan.SymColon, scan.SymDoubleColon:
var caseTerm *term var caseTerm *scan.Term
if selectorTerm != nil { if selectorTerm != nil {
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == scan.SymDoubleColon); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
currentTerm = caseTerm currentTerm = caseTerm
if tk.Sym == SymDoubleColon { if tk.Sym == scan.SymDoubleColon {
selectorTerm = nil selectorTerm = nil
} }
} }
} else { } else {
currentTerm, err = tree.addToken(tk) currentTerm, err = tree.AddToken(tk)
if tk.IsOneOfA(SymColon, SymRange) { if tk.IsOneOfA(scan.SymColon, scan.SymRange) {
// Colon outside a selector term acts like a separator // Colon outside a selector term acts like a separator
firstToken = true firstToken = true
} }
} }
default: default:
currentTerm, err = tree.addToken(tk) currentTerm, err = tree.AddToken(tk)
} }
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector { if currentTerm != nil && currentTerm.Tk.Sym != scan.SymSelector && currentTerm.Parent != nil && currentTerm.Parent.Tk.Sym != scan.SymSelector {
selectorTerm = nil selectorTerm = nil
remFlags(ctx, selectorContext) remFlags(ctx, selectorContext)
} }
@@ -557,12 +559,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
if err == nil { if err == nil {
if !tk.IsOneOf(termSymbols) { if !tk.IsOneOf(termSymbols) {
var symDesc string var symDesc string
if tk.IsSymbol(SymError) { if tk.IsSymbol(scan.SymError) {
symDesc = tk.ErrorText() symDesc = tk.ErrorText()
} else { } else {
symDesc = SymToString(tk.Sym) symDesc = scan.SymToString(tk.Sym)
} }
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc) err = tk.ErrorExpectedGotStringWithPrefix("expected one of", scan.SymListToString(termSymbols, true), symDesc)
} else { } else {
err = tk.Error() err = tk.Error()
} }
+19 -15
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// plugin.go // plugin.go
@@ -10,6 +10,8 @@ import (
"os" "os"
"plugin" "plugin"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
var pluginRegister map[string]*plugin.Plugin var pluginRegister map[string]*plugin.Plugin
@@ -39,12 +41,12 @@ func makePluginName(name string) (decorated string) {
return return
} }
func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err error) { func importPlugin(ctx kern.ExprContext, dirList []string, name string) (err error) {
var filePath string var filePath string
var p *plugin.Plugin var p *plugin.Plugin
var sym plugin.Symbol var sym plugin.Symbol
var moduleName string var moduleName string
var importFunc func(ExprContext) var importFunc func(kern.ExprContext)
var ok bool var ok bool
decoratedName := makePluginName(name) decoratedName := makePluginName(name)
@@ -71,7 +73,7 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
} }
if deps := *sym.(*[]string); len(deps) > 0 { if deps := *sym.(*[]string); len(deps) > 0 {
// var count int // var count int
if err = loadModules(dirList, deps); err != nil { if err = loadModules(ctx, dirList, deps); err != nil {
return return
} }
} }
@@ -80,33 +82,35 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
return return
} }
if importFunc, ok = sym.(func(ExprContext)); !ok { if importFunc, ok = sym.(func(kern.ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName) err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return return
} }
registerPlugin(moduleName, p) registerPlugin(moduleName, p)
importFunc(globalCtx) if globalCtx := ctx.GetGlobal(); globalCtx != nil {
importFunc(globalCtx)
}
return return
} }
func importPluginFromSearchPath(name any) (count int, err error) { func importPluginFromSearchPath(ctx kern.ExprContext, name any) (count int, err error) {
var moduleSpec any var moduleSpec any
var it Iterator var it kern.Iterator
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) dirList := buildSearchDirList(ctx, "plugin", ENV_EXPR_PLUGIN_PATH)
count = 0 count = 0
if it, err = NewIterator(name); err != nil { if it, err = NewIterator(ctx, name, nil); err != nil {
return return
} }
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok { if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil { if err = importPlugin(ctx, dirList, module); err != nil {
break break
} }
count++ count++
} else { } else {
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, kern.TypeName(moduleSpec))
break break
} }
} }
@@ -116,10 +120,10 @@ func importPluginFromSearchPath(name any) (count int, err error) {
return return
} }
func loadModules(dirList []string, moduleNames []string) (err error) { func loadModules(ctx kern.ExprContext, dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames { for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil { if err1 := importPlugin(ctx, dirList, name); err1 != nil {
if !ImportInContext(name) { if !ImportInContext(ctx, name) {
err = err1 err = err1
break break
} }
+147
View File
@@ -0,0 +1,147 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// ast.go
package scan
import (
"errors"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- ast
type Ast struct {
forest []*Term
root *Term
}
func NewAst() *Ast {
return &Ast{}
}
func (ast *Ast) TypeName() string {
return "Expression"
}
func (ast *Ast) Root() *Term {
return ast.root
}
func (ast *Ast) ToForest() {
if ast.root != nil {
if ast.forest == nil {
ast.forest = make([]*Term, 0)
}
ast.forest = append(ast.forest, ast.root)
ast.root = nil
}
}
func (ast *Ast) String() string {
var sb strings.Builder
if ast.root == nil {
sb.WriteString("(nil)")
} else {
ast.root.ToString(&sb)
}
return sb.String()
}
func (ast *Ast) AddTokens(tokens ...*Token) (err error) {
for _, tk := range tokens {
if _, err = ast.AddToken(tk); err != nil {
break
}
}
return
}
// func (expr *ast) addToken(tk *Token) (err error) {
// _, err = expr.addToken2(tk)
// return
// }
func (ast *Ast) AddToken(tk *Token) (t *Term, err error) {
if t = NewTerm(tk); t != nil {
err = ast.AddTerm(t)
} else {
err = tk.Errorf("unexpected token %q", tk.String())
}
return
}
func (ast *Ast) AddTerm(node *Term) (err error) {
if ast.root == nil {
ast.root = node
} else {
ast.root, err = ast.insert(ast.root, node)
}
return
}
func (ast *Ast) insert(tree, node *Term) (root *Term, err error) {
if tree.getPriority() < node.getPriority() {
root = tree
if tree.isComplete() {
var subRoot *Term
last := tree.removeLastChild()
if subRoot, err = ast.insert(last, node); err == nil {
subRoot.SetParent(tree)
}
} else {
node.SetParent(tree)
}
} else if !node.isLeaf() {
root = node
tree.SetParent(node)
} else {
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
}
return
}
func (ast *Ast) Finish() {
if ast.root == nil && ast.forest != nil && len(ast.forest) >= 1 {
ast.root = ast.forest[len(ast.forest)-1]
ast.forest = ast.forest[0 : len(ast.forest)-1]
}
}
func (ast *Ast) Eval(ctx kern.ExprContext) (result any, err error) {
defer func() {
if r := recover(); r != nil {
if errVal, ok := r.(error); ok {
err = errVal
} else {
err = errors.New("unexpected error while evaluating the expression")
}
}
}()
ast.Finish()
if ast.root != nil {
// initDefaultVars(ctx)
if ast.forest != nil {
for _, tree := range ast.forest {
if result, err = tree.Compute(ctx); err == nil {
ctx.UnsafeSetVar(kern.ControlLastResult, result)
} else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break
}
}
}
if err == nil {
if result, err = ast.root.Compute(ctx); err == nil {
ctx.UnsafeSetVar(kern.ControlLastResult, result)
}
}
// } else {
// err = errors.New("empty expression")
}
return
}
+78 -71
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// expr project scanner.go // expr project scanner.go
package expr package scan
import ( import (
"bufio" "bufio"
@@ -11,9 +11,12 @@ import (
"io" "io"
"strconv" "strconv"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/util"
) )
type scanner struct { type Scanner struct {
current *Token current *Token
prev *Token prev *Token
stage *Token stage *Token
@@ -23,8 +26,8 @@ type scanner struct {
translations map[Symbol]Symbol translations map[Symbol]Symbol
} }
func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *scanner) { func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *Scanner) {
inst = &scanner{ inst = &Scanner{
stream: bufio.NewReader(s), stream: bufio.NewReader(s),
row: 1, row: 1,
column: 1, column: 1,
@@ -50,7 +53,11 @@ func DefaultTranslations() map[Symbol]Symbol {
// return self.current // return self.current
// } // }
func (scanner *scanner) readChar() (ch byte, err error) { func (scanner *Scanner) Current() *Token {
return scanner.current
}
func (scanner *Scanner) readChar() (ch byte, err error) {
if ch, err = scanner.stream.ReadByte(); err == nil { if ch, err = scanner.stream.ReadByte(); err == nil {
if ch == '\n' { if ch == '\n' {
scanner.row++ scanner.row++
@@ -62,7 +69,7 @@ func (scanner *scanner) readChar() (ch byte, err error) {
return return
} }
func (scanner *scanner) unreadChar() (err error) { func (scanner *Scanner) unreadChar() (err error) {
if err = scanner.stream.UnreadByte(); err == nil { if err = scanner.stream.UnreadByte(); err == nil {
if scanner.column--; scanner.column == 0 { if scanner.column--; scanner.column == 0 {
if scanner.row--; scanner.row == 0 { if scanner.row--; scanner.row == 0 {
@@ -75,7 +82,7 @@ func (scanner *scanner) unreadChar() (err error) {
return return
} }
func (scanner *scanner) UnreadToken() (err error) { func (scanner *Scanner) UnreadToken() (err error) {
if scanner.stage == nil { if scanner.stage == nil {
scanner.stage = scanner.current scanner.stage = scanner.current
scanner.current = scanner.prev scanner.current = scanner.prev
@@ -85,7 +92,7 @@ func (scanner *scanner) UnreadToken() (err error) {
return return
} }
func (scanner *scanner) lastPos() (r, c int) { func (scanner *Scanner) LastPos() (r, c int) {
if scanner.prev != nil { if scanner.prev != nil {
r = scanner.prev.row r = scanner.prev.row
c = scanner.prev.col c = scanner.prev.col
@@ -93,11 +100,11 @@ func (scanner *scanner) lastPos() (r, c int) {
return return
} }
func (scanner *scanner) Previous() *Token { func (scanner *Scanner) Previous() *Token {
return scanner.prev return scanner.prev
} }
func (scanner *scanner) Next() (tk *Token) { func (scanner *Scanner) Next() (tk *Token) {
scanner.prev = scanner.current scanner.prev = scanner.current
tk = scanner.current tk = scanner.current
if scanner.stage != nil { if scanner.stage != nil {
@@ -109,7 +116,7 @@ func (scanner *scanner) Next() (tk *Token) {
return tk return tk
} }
func (scanner *scanner) fetchNextToken() (tk *Token) { func (scanner *Scanner) fetchNextToken() (tk *Token) {
var ch byte var ch byte
if err := scanner.skipBlanks(); err != nil { if err := scanner.skipBlanks(); err != nil {
return scanner.makeErrorToken(err) return scanner.makeErrorToken(err)
@@ -127,7 +134,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next == '>' { } else if next == '>' {
tk = scanner.moveOn(SymPlusGreater, ch, next) tk = scanner.moveOn(SymPlusGreater, ch, next)
} else { } else {
tk = scanner.makeToken(SymPlus, ch) tk = scanner.MakeToken(SymPlus, ch)
} }
case '-': case '-':
if next, _ := scanner.peek(); next == '-' { if next, _ := scanner.peek(); next == '-' {
@@ -135,7 +142,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymMinusEqual, ch, next) tk = scanner.moveOn(SymMinusEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymMinus, ch) tk = scanner.MakeToken(SymMinus, ch)
} }
case '*': case '*':
if next, _ := scanner.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
@@ -145,7 +152,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymStarEqual, ch, next) tk = scanner.moveOn(SymStarEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymStar, ch) tk = scanner.MakeToken(SymStar, ch)
} }
case '/': case '/':
if next, _ := scanner.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
@@ -157,11 +164,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
scanner.readChar() scanner.readChar()
tk = scanner.fetchOnLineComment() tk = scanner.fetchOnLineComment()
} else { } else {
tk = scanner.makeToken(SymSlash, ch) tk = scanner.MakeToken(SymSlash, ch)
} }
case '\\': case '\\':
if escape { if escape {
tk = scanner.makeToken(SymBackSlash, ch) tk = scanner.MakeToken(SymBackSlash, ch)
escape = false escape = false
} else { } else {
escape = true escape = true
@@ -172,24 +179,24 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymVertBarEqual, ch, next) tk = scanner.moveOn(SymVertBarEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymVertBar, ch) tk = scanner.MakeToken(SymVertBar, ch)
} }
case ',': case ',':
tk = scanner.makeToken(SymComma, ch) tk = scanner.MakeToken(SymComma, ch)
case '^': case '^':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymCaretEqual, ch, next) tk = scanner.moveOn(SymCaretEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymCaret, ch) tk = scanner.MakeToken(SymCaret, ch)
} }
case ':': case ':':
if next, _ := scanner.peek(); next == ':' { if next, _ := scanner.peek(); next == ':' {
tk = scanner.moveOn(SymDoubleColon, ch, next) tk = scanner.moveOn(SymDoubleColon, ch, next)
} else { } else {
tk = scanner.makeToken(SymColon, ch) tk = scanner.MakeToken(SymColon, ch)
} }
case ';': case ';':
tk = scanner.makeToken(SymSemiColon, ch) tk = scanner.MakeToken(SymSemiColon, ch)
case '.': case '.':
//if next, _ := self.peek(); next >= '0' && next <= '9' { //if next, _ := self.peek(); next >= '0' && next <= '9' {
// tk = self.parseNumber(ch) // tk = self.parseNumber(ch)
@@ -203,29 +210,29 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.moveOn(SymDoubleDot, ch, next) tk = scanner.moveOn(SymDoubleDot, ch, next)
} }
} else { } else {
tk = scanner.makeToken(SymDot, ch) tk = scanner.MakeToken(SymDot, ch)
} }
case '\'': case '\'':
if escape { if escape {
tk = scanner.makeToken(SymQuote, ch) tk = scanner.MakeToken(SymQuote, ch)
escape = false escape = false
} else { } else {
tk = scanner.fetchString(ch, true) tk = scanner.fetchString(ch, true)
} }
case '"': case '"':
if escape { if escape {
tk = scanner.makeToken(SymDoubleQuote, ch) tk = scanner.MakeToken(SymDoubleQuote, ch)
escape = false escape = false
} else { } else {
tk = scanner.fetchString(ch, true) tk = scanner.fetchString(ch, true)
} }
case '`': case '`':
tk = scanner.makeToken(SymBackTick, ch) tk = scanner.MakeToken(SymBackTick, ch)
case '!': case '!':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymNotEqual, ch, next) tk = scanner.moveOn(SymNotEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymExclamation, ch) tk = scanner.MakeToken(SymExclamation, ch)
} }
case '?': case '?':
if next, _ := scanner.peek(); next == '?' { if next, _ := scanner.peek(); next == '?' {
@@ -235,7 +242,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next == '!' { } else if next == '!' {
tk = scanner.moveOn(SymQuestionExclam, ch, next) tk = scanner.moveOn(SymQuestionExclam, ch, next)
} else { } else {
tk = scanner.makeToken(SymQuestion, ch) tk = scanner.MakeToken(SymQuestion, ch)
} }
case '&': case '&':
if next, _ := scanner.peek(); next == '&' { if next, _ := scanner.peek(); next == '&' {
@@ -243,16 +250,16 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next, _ = scanner.peek(); next == '=' { } else if next, _ = scanner.peek(); next == '=' {
tk = scanner.moveOn(SymAmpersandEqual, ch, next) tk = scanner.moveOn(SymAmpersandEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymAmpersand, ch) tk = scanner.MakeToken(SymAmpersand, ch)
} }
case '%': case '%':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymPercEqual, ch, next) tk = scanner.moveOn(SymPercEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymPercent, ch) tk = scanner.MakeToken(SymPercent, ch)
} }
case '#': case '#':
tk = scanner.makeToken(SymHash, ch) tk = scanner.MakeToken(SymHash, ch)
case '@': case '@':
if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') { if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
scanner.readChar() scanner.readChar()
@@ -265,15 +272,15 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next == '@' { } else if next == '@' {
tk = scanner.moveOn(SymDoubleAt, ch, next) tk = scanner.moveOn(SymDoubleAt, ch, next)
} else { } else {
tk = scanner.makeToken(SymAt, ch) tk = scanner.MakeToken(SymAt, ch)
} }
case '_': case '_':
tk = scanner.makeToken(SymUndescore, ch) tk = scanner.MakeToken(SymUndescore, ch)
case '=': case '=':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymDoubleEqual, ch, next) tk = scanner.moveOn(SymDoubleEqual, ch, next)
} else { } else {
tk = scanner.makeToken(SymEqual, ch) tk = scanner.MakeToken(SymEqual, ch)
} }
case '<': case '<':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
@@ -292,7 +299,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
} else if next == '+' { } else if next == '+' {
tk = scanner.moveOn(SymLessPlus, ch, next) tk = scanner.moveOn(SymLessPlus, ch, next)
} else { } else {
tk = scanner.makeToken(SymLess, ch) tk = scanner.MakeToken(SymLess, ch)
} }
case '>': case '>':
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
@@ -307,7 +314,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.accept(SymDoubleGreater, ch, next) tk = scanner.accept(SymDoubleGreater, ch, next)
} }
} else { } else {
tk = scanner.makeToken(SymGreater, ch) tk = scanner.MakeToken(SymGreater, ch)
} }
case '$': case '$':
if next, _ := scanner.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
@@ -324,26 +331,26 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
scanner.readChar() scanner.readChar()
tk = scanner.fetchIdentifier(next) tk = scanner.fetchIdentifier(next)
} else { } else {
tk = scanner.makeToken(SymDollar, ch) tk = scanner.MakeToken(SymDollar, ch)
} }
case '(': case '(':
// if next, _ := scanner.peek(); next == ')' { // if next, _ := scanner.peek(); next == ')' {
// tk = scanner.moveOn(SymOpenClosedRound, ch, next) // tk = scanner.moveOn(SymOpenClosedRound, ch, next)
// } else { // } else {
tk = scanner.makeToken(SymOpenRound, ch) tk = scanner.MakeToken(SymOpenRound, ch)
// } // }
case ')': case ')':
tk = scanner.makeToken(SymClosedRound, ch) tk = scanner.MakeToken(SymClosedRound, ch)
case '[': case '[':
tk = scanner.makeToken(SymOpenSquare, ch) tk = scanner.MakeToken(SymOpenSquare, ch)
case ']': case ']':
tk = scanner.makeToken(SymClosedSquare, ch) tk = scanner.MakeToken(SymClosedSquare, ch)
case '{': case '{':
tk = scanner.makeToken(SymOpenBrace, ch) tk = scanner.MakeToken(SymOpenBrace, ch)
case '}': case '}':
tk = scanner.makeToken(SymClosedBrace, ch) tk = scanner.MakeToken(SymClosedBrace, ch)
case '~': case '~':
tk = scanner.makeToken(SymTilde, ch) tk = scanner.MakeToken(SymTilde, ch)
case 0: case 0:
if escape { if escape {
tk = scanner.makeErrorToken(errors.New("incomplete escape sequence")) tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
@@ -370,7 +377,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
return return
} }
func (scanner *scanner) sync(err error) error { func (scanner *Scanner) sync(err error) error {
if err == nil { if err == nil {
err = scanner.unreadChar() err = scanner.unreadChar()
} }
@@ -393,7 +400,7 @@ func isHexDigit(ch byte) bool {
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
} }
func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) { func (scanner *Scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) {
var ch byte var ch byte
var digitType string var digitType string
firstCh = currentFirstCh firstCh = currentFirstCh
@@ -432,7 +439,7 @@ func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int
return return
} }
func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) { func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sym Symbol = SymInteger var sym Symbol = SymInteger
@@ -503,12 +510,12 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
if sym == SymFloat { if sym == SymFloat {
value, err = strconv.ParseFloat(txt, 64) value, err = strconv.ParseFloat(txt, 64)
} else if sym == SymFraction { } else if sym == SymFraction {
value, err = makeGeneratingFraction(txt) value, err = kern.MakeGeneratingFraction(txt)
} else { } else {
value, err = strconv.ParseInt(txt, numBase, 64) value, err = strconv.ParseInt(txt, numBase, 64)
} }
if err == nil { if err == nil {
tk = scanner.makeValueToken(sym, txt, value) tk = scanner.MakeValueToken(sym, txt, value)
} else { } else {
tk = scanner.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
@@ -516,7 +523,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
return return
} }
func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) { func (scanner *Scanner) fetchIdentifier(firstCh byte) (tk *Token) {
var err error var err error
var sb strings.Builder var sb strings.Builder
for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = scanner.readChar() { for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = scanner.readChar() {
@@ -533,14 +540,14 @@ func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
if sym, ok := keywords[uptxt]; ok { if sym, ok := keywords[uptxt]; ok {
tk = scanner.makeKeywordToken(sym, uptxt) tk = scanner.makeKeywordToken(sym, uptxt)
} else if uptxt == `TRUE` { } else if uptxt == `TRUE` {
tk = scanner.makeValueToken(SymBool, txt, true) tk = scanner.MakeValueToken(SymBool, txt, true)
} else if uptxt == `FALSE` { } else if uptxt == `FALSE` {
tk = scanner.makeValueToken(SymBool, txt, false) tk = scanner.MakeValueToken(SymBool, txt, false)
} else if ch, _ := scanner.peek(); ch == '(' { } else if ch, _ := scanner.peek(); ch == '(' {
scanner.readChar() scanner.readChar()
tk = scanner.makeValueToken(SymFuncCall, txt+"(", txt) tk = scanner.MakeValueToken(SymFuncCall, txt+"(", txt)
} else { } else {
tk = scanner.makeValueToken(SymIdentifier, txt, txt) tk = scanner.MakeValueToken(SymIdentifier, txt, txt)
} }
} }
@@ -560,20 +567,20 @@ func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
return return
} }
func (scanner *scanner) fetchBlockComment() *Token { func (scanner *Scanner) fetchBlockComment() *Token {
return scanner.fetchUntil(SymComment, false, '*', '/') return scanner.fetchUntil(SymComment, false, '*', '/')
} }
func (scanner *scanner) fetchOnLineComment() *Token { func (scanner *Scanner) fetchOnLineComment() *Token {
return scanner.fetchUntil(SymComment, true, '\n') return scanner.fetchUntil(SymComment, true, '\n')
} }
func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) { func (scanner *Scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sb strings.Builder var sb strings.Builder
var value string var value string
ring := NewByteSlider(len(endings)) ring := util.NewByteSlider(len(endings))
endReached := false endReached := false
for ch, err = scanner.readChar(); err == nil && !endReached; { for ch, err = scanner.readChar(); err == nil && !endReached; {
sb.WriteByte(ch) sb.WriteByte(ch)
@@ -591,14 +598,14 @@ func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (
} }
if endReached { if endReached {
tk = scanner.makeValueToken(sym, "", value) tk = scanner.MakeValueToken(sym, "", value)
} else { } else {
tk = scanner.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
return return
} }
func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) { func (scanner *Scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
var err error var err error
var ch, prev byte var ch, prev byte
var sb strings.Builder var sb strings.Builder
@@ -637,15 +644,15 @@ func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
} else { } else {
txt := sb.String() txt := sb.String()
if addQuote { if addQuote {
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt) tk = scanner.MakeValueToken(SymString, `"`+txt+`"`, txt)
} else { } else {
tk = scanner.makeValueToken(SymString, txt, txt) tk = scanner.MakeValueToken(SymString, txt, txt)
} }
} }
return return
} }
func (scanner *scanner) peek() (next byte, err error) { func (scanner *Scanner) peek() (next byte, err error) {
var one []byte var one []byte
if one, err = scanner.stream.Peek(1); err == nil { if one, err = scanner.stream.Peek(1); err == nil {
next = one[0] next = one[0]
@@ -653,7 +660,7 @@ func (scanner *scanner) peek() (next byte, err error) {
return return
} }
func (scanner *scanner) skipBlanks() (err error) { func (scanner *Scanner) skipBlanks() (err error) {
var one []byte var one []byte
for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) { for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) {
scanner.readChar() scanner.readChar()
@@ -661,7 +668,7 @@ func (scanner *scanner) skipBlanks() (err error) {
return return
} }
func (scanner *scanner) translate(sym Symbol) Symbol { func (scanner *Scanner) translate(sym Symbol) Symbol {
if scanner.translations != nil { if scanner.translations != nil {
if translatedSym, ok := scanner.translations[sym]; ok { if translatedSym, ok := scanner.translations[sym]; ok {
return translatedSym return translatedSym
@@ -670,7 +677,7 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
return sym return sym
} }
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
// for i := 1; i < len(chars); i++ { // for i := 1; i < len(chars); i++ {
if len(chars) > 1 { if len(chars) > 1 {
@@ -680,26 +687,26 @@ func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
return return
} }
func (scanner *scanner) accept(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (scanner *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) { func (scanner *Scanner) MakeToken(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (scanner *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) { func (scanner *Scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
return return
} }
func (scanner *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) { func (scanner *Scanner) MakeValueToken(sym Symbol, source string, value any) (tk *Token) {
tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value) tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
return return
} }
func (scanner *scanner) makeErrorToken(err error) *Token { func (scanner *Scanner) makeErrorToken(err error) *Token {
return NewErrorToken(scanner.row, scanner.column, err) return NewErrorToken(scanner.row, scanner.column, err)
} }
+231
View File
@@ -0,0 +1,231 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// Symbol.go
package scan
import (
"strings"
)
var symbolMap map[Symbol]symbolSpec
type SymbolClass int16
const (
SymClassOperator SymbolClass = iota
SymClassCommand
SymClassIdentifier
SymClassDelimiter
SymClassParenthesis
SymClassDeclaration
SymClassValue
SymClassOther
)
type symbolSpec struct {
repr string
kind SymbolClass
opType TermPosition
}
func init() {
symbolMap = map[Symbol]symbolSpec{
SymUnknown: {"<unknown>", SymClassOther, PosLeaf}, // -1: Unknown symbol
SymNone: {"<null>", SymClassOther, PosLeaf}, // 0: Null value for variable of type symbol
SymError: {"<error>", SymClassOther, PosLeaf}, // 1: Error reading from stream
SymEos: {"<eos>", SymClassOther, PosLeaf}, // 2: End of stream
SymMinus: {"-", SymClassOperator, PosInfix}, // 3: '-'
SymMinusEqual: {"-=", SymClassOperator, PosInfix}, // 4: '-='
SymDoubleMinus: {"--", SymClassOperator, PosPostfix}, // 5: '--'
SymPlus: {"+", SymClassOperator, PosInfix}, // 6: '+'
SymPlusEqual: {"+=", SymClassOperator, PosInfix}, // 7: '+='
SymDoublePlus: {"++", SymClassOperator, PosPostfix}, // 8: '++'
SymStar: {"*", SymClassOperator, PosInfix}, // 9: '*'
SymDoubleStar: {"**", SymClassOperator, PosInfix}, // 10: '**'
SymSlash: {"/", SymClassOperator, PosInfix}, // 11: '/'
SymBackSlash: {"\\", SymClassOperator, PosLeaf}, // 12: '\'
SymVertBar: {"|", SymClassOperator, PosInfix}, // 13: '|'
SymDoubleVertBar: {"||", SymClassOperator, PosInfix}, // 14: '||'
SymComma: {",", SymClassOperator, PosInfix}, // 15: ','
SymColon: {":", SymClassOperator, PosInfix}, // 16: ':'
SymSemiColon: {";", SymClassOperator, PosInfix}, // 17: ';'
SymDot: {".", SymClassOperator, PosInfix}, // 18: '.'
SymDotSlash: {"./", SymClassOperator, PosInfix}, // 19: './'
SymQuote: {"'", SymClassDelimiter, PosLeaf}, // 20: '\''
SymDoubleQuote: {"\"", SymClassDelimiter, PosLeaf}, // 21: '"'
SymBackTick: {"`", SymClassDelimiter, PosLeaf}, // 22: '`'
SymExclamation: {"!", SymClassOperator, PosPostfix}, // 23: '!'
SymQuestion: {"?", SymClassOperator, PosInfix}, // 24: '?'
SymAmpersand: {"&", SymClassOperator, PosInfix}, // 25: '&'
SymDoubleAmpersand: {"&&", SymClassOperator, PosInfix}, // 26: '&&'
SymPercent: {"%", SymClassOperator, PosInfix}, // 27: '%'
SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@'
SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_'
SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '='
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 31: '=='
SymLess: {"<", SymClassOperator, PosInfix}, // 32: '<'
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 33: '<='
SymGreater: {">", SymClassOperator, PosInfix}, // 34: '>'
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 35: '>='
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 36: '<>'
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 37: '!='
SymDollar: {"$", SymClassOperator, PosPrefix}, // 38: '$'
SymHash: {"#", SymClassOperator, PosPrefix}, // 39: '#'
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 40: '('
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 41: ')'
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 42: '['
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 43: ']'
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 44: '{'
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 45: '}'
SymTilde: {"~", SymClassOperator, PosPrefix}, // 46: '~'
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 47: '??'
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 48: '?='
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 49: '?!'
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 50: '@@'
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 51: '::'
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 52: '>>'
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 53: '<<'
SymCaret: {"^", SymClassOperator, PosInfix}, // 54: '^'
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 55: '$('
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 56: '()'
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 57: '$$'
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 58: '..'
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 59: '...'
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 60: '*='
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 61: '/='
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 62: '%='
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 63: '<<='
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 64: '>>='
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 65: '&='
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 65: '|='
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 66: '^='
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 67: '+>'
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 68: '<+'
SymPreInc: {"++", SymClassOperator, PosPrefix}, // : '++'
SymPreDec: {"--", SymClassOperator, PosPrefix}, // : '--'
// SymChangeSign
// SymUnchangeSign
// SymIdentifier
// SymBool
// SymInteger
// SymVariable
// SymFloat
// SymFraction
// SymString
// SymIterator
// SymOr: "or",
// SymAnd: "and",
// SymNot: "not",
// SymComment
// SymFuncCall
// SymFuncDef
// SymList
// SymDict
// SymIndex
// SymExpression
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
// // SymOpenComment // 0: '/*'
// // SymClosedComment // 0: '*/'
// // SymOneLineComment // 0: '//'
// keywordBase
SymKwAnd: {"and", SymClassOperator, PosInfix},
SymKwNot: {"not", SymClassOperator, PosInfix},
SymKwOr: {"or", SymClassOperator, PosInfix},
SymKwBut: {"but", SymClassOperator, PosInfix},
SymKwMap: {"map", SymClassOperator, PosInfix},
SymKwFilter: {"filter", SymClassOperator, PosInfix},
SymKwDigest: {"digest", 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},
}
}
func SymToString(sym Symbol) string {
if s, ok := symbolMap[sym]; ok {
return s.repr
}
return ""
}
func SymListToString(symList []Symbol, quote bool) string {
var sb strings.Builder
if len(symList) == 0 {
sb.WriteString("<nothing>")
} else {
for _, sym := range symList {
if sb.Len() > 0 {
sb.WriteByte(',')
sb.WriteByte(' ')
}
if quote {
sb.WriteByte('`')
}
sb.WriteString(SymToString(sym))
if quote {
sb.WriteByte('`')
}
}
}
return sb.String()
}
func StringEndsWithOperator(s string) bool {
return endingOperator(s) != SymNone
}
// func endingOperator(s string) (sym Symbol) {
// var matchLength = 0
// sym = SymNone
// lower := strings.TrimRight(strings.ToLower(s), " \t")
// for symbol, spec := range symbolMap {
// if strings.HasSuffix(lower, spec.repr) {
// if len(spec.repr) > matchLength {
// matchLength = len(spec.repr)
// if spec.kind == symClassOperator && (spec.opType == PosInfix || spec.opType == PosPrefix) {
// sym = symbol
// } else {
// sym = SymNone
// }
// }
// }
// }
// return
// }
func endingOperator(s string) (sym Symbol) {
var matchLength = 0
var repr string
sym = SymNone
lower := strings.TrimRight(strings.ToLower(s), " \t")
for symbol, spec := range symbolMap {
if len(spec.repr) > matchLength || repr == spec.repr {
if strings.HasSuffix(lower, spec.repr) {
if isNotEndingSymbol(spec) && repr != spec.repr {
repr = spec.repr
matchLength = len(spec.repr)
sym = symbol
} else {
sym = SymNone
break
// matchLength = 0
}
}
}
}
return
}
func isNotEndingSymbol(spec symbolSpec) bool {
return (spec.kind == SymClassOperator && (spec.opType == PosInfix || spec.opType == PosPrefix)) ||
(spec.kind == SymClassParenthesis && spec.opType == PosPrefix)
}
+10 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// Symbol.go // Symbol.go
package expr package scan
type Symbol int16 type Symbol int16
@@ -120,6 +120,10 @@ const (
SymKwIn SymKwIn
SymKwInclude SymKwInclude
SymKwMap SymKwMap
SymKwFilter
SymKwDigest
SymKwGroupBy
SymKwJoin
SymKwNil SymKwNil
SymKwUnset SymKwUnset
) )
@@ -137,9 +141,13 @@ func init() {
"IN": SymKwIn, "IN": SymKwIn,
"INCLUDE": SymKwInclude, "INCLUDE": SymKwInclude,
"MAP": SymKwMap, "MAP": SymKwMap,
"FILTER": SymKwFilter,
"NOT": SymKwNot, "NOT": SymKwNot,
"OR": SymKwOr, "OR": SymKwOr,
"NIL": SymKwNil, "NIL": SymKwNil,
"UNSET": SymKwUnset, "UNSET": SymKwUnset,
"DIGEST": SymKwDigest,
"JOIN": SymKwJoin,
"GROUPBY": SymKwGroupBy,
} }
} }
+5 -3
View File
@@ -1,14 +1,16 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// t_scanner_test.go // t_scanner_test.go
package expr package scan
import ( import (
"errors" "errors"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func TestScanner(t *testing.T) { func TestScanner(t *testing.T) {
@@ -60,7 +62,7 @@ func TestScanner(t *testing.T) {
/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil}, /* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil},
/* 39 */ {`"string"`, SymString, "string", nil}, /* 39 */ {`"string"`, SymString, "string", nil},
/* 40 */ {`identifier`, SymIdentifier, "identifier", nil}, /* 40 */ {`identifier`, SymIdentifier, "identifier", nil},
/* 41 */ {`1.2(3)`, SymFraction, newFraction(37, 30), nil}, /* 41 */ {`1.2(3)`, SymFraction, kern.NewFraction(37, 30), nil},
} }
for i, input := range inputs { for i, input := range inputs {
@@ -1,23 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// op-registry.go // term-constructor-registry.go
package expr package scan
const initialRegistryCapacity = 10 const initialRegistryCapacity = 10
type termContructor func(tk *Token) *term type termContructor func(tk *Token) *Term
var constructorRegistry map[Symbol]termContructor = nil var constructorRegistry map[Symbol]termContructor = nil
func registerTermConstructor(sym Symbol, constructor termContructor) { func RegisterTermConstructor(sym Symbol, constructor termContructor) {
if constructorRegistry == nil { if constructorRegistry == nil {
constructorRegistry = make(map[Symbol]termContructor, initialRegistryCapacity) constructorRegistry = make(map[Symbol]termContructor, initialRegistryCapacity)
} }
constructorRegistry[sym] = constructor constructorRegistry[sym] = constructor
} }
func newTerm(tk *Token) (inst *term) { func NewTerm(tk *Token) (inst *Term) {
if constructorRegistry != nil { if constructorRegistry != nil {
if construct, exists := constructorRegistry[tk.Sym]; exists { if construct, exists := constructorRegistry[tk.Sym]; exists {
inst = construct(tk) inst = construct(tk)
+301
View File
@@ -0,0 +1,301 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// term.go
package scan
import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
type TermPriority uint32
const (
PriNone TermPriority = iota
PriRange
PriIterOp // map, filter, digest, etc
PriBut
PriAssign
PriInsert
PriOr
PriAnd
PriNot
PriRelational
PriBitwiseOr
PriBitwiseAnd
PriBitwiseNot
PriSum
PriProduct
PriFraction
PriSelector
PriBinShift
PriSign
PriFact
PriIterValue
PriDefault
PriIncDec
PriDot
PriDereference
PriValue
PriBitwiseXor = PriBitwiseOr
)
type TermPosition uint8
const (
PosLeaf TermPosition = iota
PosInfix
PosPrefix
PosPostfix
PosMultifix
)
type EvalFuncType func(ctx kern.ExprContext, self *Term) (v any, err error)
type Term struct {
Tk Token
Parent *Term
Children []*Term
Position TermPosition // operator position: leaf, infix, prefix, postfix, multifix
Priority TermPriority // operator priority: higher value means higher priority
EvalFunc EvalFuncType
}
func (t *Term) Clone() (d *Term) {
var children []*Term
if t.Children != nil {
children = make([]*Term, len(t.Children))
for i, c := range t.Children {
children[i] = c.Clone()
}
}
d = &Term{
Tk: *t.Tk.Clone(),
Parent: t.Parent,
Children: children,
Position: t.Position,
Priority: t.Priority,
EvalFunc: t.EvalFunc,
}
return
}
func (t *Term) String() string {
var sb strings.Builder
t.ToString(&sb)
return sb.String()
}
func (t *Term) ToString(sb *strings.Builder) {
if t.Position == PosLeaf {
sb.WriteString(t.Tk.String())
} else {
sb.WriteByte('[')
sb.WriteString(t.Tk.String())
if t.Children != nil {
sb.WriteByte('(')
for i, c := range t.Children {
if i > 0 {
sb.WriteByte(' ')
}
c.ToString(sb)
}
sb.WriteByte(')')
}
sb.WriteByte(']')
}
}
func (t *Term) GetChildCount() (count int) {
if t.Position == PosLeaf || t.Children == nil {
count = 0
} else {
count = len(t.Children)
}
return
}
func (t *Term) GetRoom() (room int) {
switch t.Position {
case PosLeaf:
room = 0
case PosInfix:
room = 2
case PosPostfix, PosPrefix:
room = 1
default:
panic("Invalid node position")
}
return
}
func (t *Term) isComplete() bool {
return t.GetChildCount() == t.GetRoom()
}
func (t *Term) removeLastChild() (child *Term) {
if t.Children != nil {
child = t.Children[len(t.Children)-1]
t.Children = t.Children[0 : len(t.Children)-1]
} else {
panic("Can't get last child")
}
return
}
func (t *Term) isLeaf() bool {
return t.Position == PosLeaf
}
func (t *Term) getPriority() TermPriority {
return t.Priority
}
func (t *Term) SetParent(parent *Term) {
t.Parent = parent
if parent != nil && len(parent.Children) < cap(parent.Children) {
parent.Children = append(parent.Children, t)
}
}
func (t *Term) Symbol() Symbol {
return t.Tk.Sym
}
func (t *Term) Source() string {
return t.Tk.source
}
func (t *Term) Value() any {
return t.Tk.Value
}
func (t *Term) GetChild(index int) (c kern.Term) {
if index >= 0 && index < len(t.Children) {
c = t.Children[index]
}
return
}
func (t *Term) GetChildSource(index int) (s string) {
if index >= 0 && index < len(t.Children) {
s = t.Children[index].Tk.source
}
return
}
func (t *Term) IsAssign() bool {
return t.Symbol() == SymEqual
}
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())
} else {
v, err = t.EvalFunc(ctx, t)
}
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)
rightType := kern.TypeName(rightValue)
rightText := kern.GetFormatted(rightValue, kern.Truncate)
return t.Tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
leftText, leftType,
rightText, rightType,
t.Source())
}
func (t *Term) ErrIncompatibleType(value any, side string) error {
return t.Tk.Errorf(
"operator %q does not support operand '%v' [%s] on its %s side",
t.Source(), value, kern.TypeName(value), side)
}
func (t *Term) ErrIncompatiblePrefixPostfixType(value any) error {
return t.Tk.Errorf(
"prefix/postfix operator %q does not support operand '%v' [%s]",
t.Source(), value, kern.TypeName(value))
}
func (t *Term) ErrDivisionByZero() error {
return t.Tk.Errorf("division by zero")
}
func (t *Term) ErrKeyNotFound(key any) error {
return t.Tk.Errorf("key '%v' not found", key)
}
func (t *Term) Errorf(template string, args ...any) (err error) {
err = t.Tk.Errorf(template, args...)
return
}
func (t *Term) CheckOperands() (err error) {
switch t.Position {
case PosInfix:
if t.Children == nil || len(t.Children) != 2 || t.anyChildrenNil() {
err = t.Tk.Errorf("infix operator %q requires two non-nil operands, got %d", t.Source(), t.GetChildCount())
}
case PosPrefix:
if t.Children == nil || len(t.Children) != 1 || t.Children[0] == nil {
err = t.Tk.Errorf("prefix operator %q requires one non-nil operand", t.Tk.String())
}
case PosPostfix:
if t.Children == nil || len(t.Children) != 1 || t.anyChildrenNil() {
err = t.Tk.Errorf("postfix operator %q requires one non-nil operand", t.Tk.String())
}
case PosMultifix:
if len(t.Children) < 3 || t.anyChildrenNil() {
err = t.Tk.Errorf("infix operator %q requires at least three not operands, got %d", t.Source(), t.GetChildCount())
}
}
return
}
func (t *Term) anyChildrenNil() bool {
for _, child := range t.Children {
if child == nil {
return true
}
}
return false
}
func (t *Term) EvalInfix(ctx kern.ExprContext) (leftValue, rightValue any, err error) {
if err = t.CheckOperands(); err == nil {
if leftValue, err = t.Children[0].Compute(ctx); err == nil {
rightValue, err = t.Children[1].Compute(ctx)
}
}
return
}
func (t *Term) EvalPrefix(ctx kern.ExprContext) (childValue any, err error) {
if err = t.CheckOperands(); err == nil {
childValue, err = t.Children[0].Compute(ctx)
}
return
}
// NOTE Temporary solution to support function parameters with default value
func (t *Term) ForceChild(c *Term) {
if t.Children == nil {
t.Children = make([]*Term, 0, 1)
}
t.Children = append(t.Children, c)
}
+14 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// token.go // token.go
package expr package scan
import ( import (
"fmt" "fmt"
@@ -51,6 +51,18 @@ func NewErrorToken(row, col int, err error) *Token {
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
} }
func (tk *Token) Source() string {
return tk.source
}
func (tk *Token) Row() int {
return tk.row
}
func (tk *Token) Col() int {
return tk.col
}
func (tk *Token) Clone() (c *Token) { func (tk *Token) Clone() (c *Token) {
return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value) return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
} }

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