Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99c1adc434 | |||
| 5585b496fb | |||
| acd4f8487d | |||
| 7f34ccf955 | |||
| f63ff5953e | |||
| b9d37a5b4c | |||
| 23b8eec74a | |||
| bb6b6d17ec | |||
| 53acacbadf | |||
| 2ebc52891c | |||
| 3b2ef7927b | |||
| d5ced343c4 | |||
| 3ac8cab275 | |||
| 6c5e9db34b | |||
| 78871641d0 | |||
| dacbec677a | |||
| 75ed88915d | |||
| f2d1f23774 | |||
| edd90054d7 | |||
| 610e2df5f5 | |||
| 32c0b45255 | |||
| 75a3f220df | |||
| 116b54836f | |||
| 8787973de0 | |||
| 4d910dd069 | |||
| f100adead3 | |||
| 7d2cf1e687 | |||
| 49728307f3 | |||
| 20dc502438 | |||
| ce7bfc5e3f | |||
| 6e98bdd16b |
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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())
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
Reference in New Issue
Block a user