Compare commits
45 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 | |||
| 6ee365bacc | |||
| 20d8236325 | |||
| c39ee7cec0 | |||
| 9e4252173b | |||
| 2b80ba6789 | |||
| d7247f97c5 | |||
| 02df7f1c1f | |||
| b6b09b2fb1 | |||
| 0677180456 | |||
| b6952444ab | |||
| 6c3a071a02 | |||
| d7d03b4af8 | |||
| 49c5cb6a22 | |||
| 53d805a832 |
+1
-1
@@ -144,4 +144,4 @@ Variables and functions can be added to a context both programmatically and ad a
|
||||
|
||||
== Expressions syntax
|
||||
|
||||
See #TODO link to doc/Expr.html#
|
||||
See https://cdn.paas.portale-stac.it/howto/go-pkg/expr/doc/Expr.html[Expr documentation] for a complete reference of the expression syntax and available functions.
|
||||
|
||||
@@ -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.
|
||||
|
||||
// function.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
// ---- Linking with Expr functions
|
||||
type exprFunctor struct {
|
||||
baseFunctor
|
||||
params []ExprFuncParam
|
||||
expr Expr
|
||||
defCtx ExprContext
|
||||
kern.BaseFunctor
|
||||
params []kern.ExprFuncParam
|
||||
expr kern.Expr
|
||||
defCtx kern.ExprContext
|
||||
}
|
||||
|
||||
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
|
||||
func (functor *exprFunctor) GetParams() (params []kern.ExprFuncParam) {
|
||||
return functor.params
|
||||
}
|
||||
|
||||
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
||||
var defCtx ExprContext
|
||||
func newExprFunctor(e kern.Expr, params []kern.ExprFuncParam, ctx kern.ExprContext) *exprFunctor {
|
||||
var defCtx kern.ExprContext
|
||||
if ctx != nil {
|
||||
defCtx = ctx
|
||||
}
|
||||
@@ -28,17 +32,17 @@ func (functor *exprFunctor) TypeName() string {
|
||||
return "ExprFunctor"
|
||||
}
|
||||
|
||||
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
||||
func (functor *exprFunctor) GetDefinitionContext() kern.ExprContext {
|
||||
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
|
||||
for _, p := range functor.params {
|
||||
if arg, exists := args[p.Name()]; exists {
|
||||
if funcArg, ok := arg.(Functor); ok {
|
||||
if funcArg, ok := arg.(kern.Functor); ok {
|
||||
paramSpecs := funcArg.GetParams()
|
||||
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
||||
ctx.RegisterFunc(p.Name(), funcArg, kern.TypeAny, paramSpecs)
|
||||
} else {
|
||||
ctx.UnsafeSetVar(p.Name(), arg)
|
||||
}
|
||||
@@ -51,7 +55,7 @@ func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[s
|
||||
}
|
||||
}
|
||||
if missing != nil {
|
||||
err = ErrMissingParams(name, missing)
|
||||
err = kern.ErrMissingParams(name, missing)
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
+135
-93
@@ -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.
|
||||
|
||||
// builtin-base.go
|
||||
@@ -9,81 +9,84 @@ import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
const (
|
||||
ParamDenominator = "denominator"
|
||||
)
|
||||
|
||||
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = args[ParamValue] == nil
|
||||
func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = args[kern.ParamValue] == nil
|
||||
return
|
||||
}
|
||||
|
||||
func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsInteger(args[ParamValue])
|
||||
func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsInteger(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsFloat(args[ParamValue])
|
||||
func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsFloat(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsBool(args[ParamValue])
|
||||
func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsBool(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsString(args[ParamValue])
|
||||
func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsString(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsFract(args[ParamValue])
|
||||
func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsFraction(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsRational(args[ParamValue])
|
||||
func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsRational(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsList(args[ParamValue])
|
||||
func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsList(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = IsDict(args[ParamValue])
|
||||
func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = kern.IsDict(args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
|
||||
func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[ParamValue].(type) {
|
||||
func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[kern.ParamValue].(type) {
|
||||
case int64:
|
||||
result = (v != 0)
|
||||
case *FractionType:
|
||||
result = v.num != 0
|
||||
case *kern.FractionType:
|
||||
result = v.N() != 0
|
||||
case float64:
|
||||
result = v != 0.0
|
||||
case bool:
|
||||
result = v
|
||||
case string:
|
||||
result = len(v) > 0
|
||||
case *ListType:
|
||||
case *kern.ListType:
|
||||
result = len(*v) > 0
|
||||
case *DictType:
|
||||
case *kern.DictType:
|
||||
result = len(*v) > 0
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "bool")
|
||||
err = kern.ErrCantConvert(name, v, "bool")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[ParamValue].(type) {
|
||||
func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[kern.ParamValue].(type) {
|
||||
case int64:
|
||||
result = v
|
||||
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 {
|
||||
result = int64(i)
|
||||
}
|
||||
case *FractionType:
|
||||
result = int64(v.num / v.den)
|
||||
case *kern.FractionType:
|
||||
result = int64(v.N() / v.D())
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "int")
|
||||
err = kern.ErrCantConvert(name, v, "int")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[ParamValue].(type) {
|
||||
func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[kern.ParamValue].(type) {
|
||||
case int64:
|
||||
result = float64(v)
|
||||
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 {
|
||||
result = f
|
||||
}
|
||||
case *FractionType:
|
||||
result = v.toFloat()
|
||||
case *kern.FractionType:
|
||||
result = v.ToFloat()
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "float")
|
||||
err = kern.ErrCantConvert(name, v, "float")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[ParamValue].(type) {
|
||||
func stringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[kern.ParamValue].(type) {
|
||||
case int64:
|
||||
result = strconv.FormatInt(v, 10)
|
||||
case float64:
|
||||
@@ -146,130 +149,169 @@ func stringFunc(ctx ExprContext, name string, args map[string]any) (result any,
|
||||
}
|
||||
case string:
|
||||
result = v
|
||||
case *FractionType:
|
||||
case *kern.FractionType:
|
||||
result = v.ToString(0)
|
||||
case Formatter:
|
||||
case kern.Formatter:
|
||||
result = v.ToString(0)
|
||||
case fmt.Stringer:
|
||||
result = v.String()
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "string")
|
||||
err = kern.ErrCantConvert(name, v, "string")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[ParamValue].(type) {
|
||||
func fractFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
switch v := args[kern.ParamValue].(type) {
|
||||
case int64:
|
||||
var den int64 = 1
|
||||
|
||||
var ok bool
|
||||
if den, ok = args[ParamDenominator].(int64); !ok {
|
||||
err = ErrExpectedGot(name, "integer", args[ParamDenominator])
|
||||
err = kern.ErrExpectedGot(name, "integer", args[ParamDenominator])
|
||||
} else if den == 0 {
|
||||
err = ErrFuncDivisionByZero(name)
|
||||
err = kern.ErrFuncDivisionByZero(name)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
result = newFraction(v, den)
|
||||
result = kern.NewFraction(v, den)
|
||||
}
|
||||
case float64:
|
||||
result, err = float64ToFraction(v)
|
||||
result, err = kern.Float64ToFraction(v)
|
||||
case bool:
|
||||
if v {
|
||||
result = newFraction(1, 1)
|
||||
result = kern.NewFraction(1, 1)
|
||||
} else {
|
||||
result = newFraction(0, 1)
|
||||
result = kern.NewFraction(0, 1)
|
||||
}
|
||||
case string:
|
||||
result, err = makeGeneratingFraction(v)
|
||||
case *FractionType:
|
||||
result, err = kern.MakeGeneratingFraction(v)
|
||||
case *kern.FractionType:
|
||||
result = v
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "float")
|
||||
err = kern.ErrCantConvert(name, v, "float")
|
||||
}
|
||||
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
|
||||
// }
|
||||
|
||||
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
var expr Expr
|
||||
func evalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[kern.ParamSource].(string); ok {
|
||||
var ast kern.Expr
|
||||
|
||||
parser := NewParser()
|
||||
if ctx == nil {
|
||||
ctx = NewSimpleStore()
|
||||
ctx = NewSimpleStoreWithoutGlobalContext()
|
||||
}
|
||||
|
||||
r := strings.NewReader(source)
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
|
||||
if expr, err = parser.Parse(scanner); err == nil {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
result, err = expr.Eval(ctx)
|
||||
if ast, err = parser.Parse(scanner); err == nil {
|
||||
CtrlEnable(ctx, kern.ControlExportAll)
|
||||
result, err = ast.Eval(ctx)
|
||||
}
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
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 ok bool
|
||||
|
||||
if varName, ok = args[ParamName].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
|
||||
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||
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)
|
||||
// } 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 {
|
||||
err = ErrUnknownVar(name, varName)
|
||||
err = kern.ErrUnknownVar(name, varName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//// import
|
||||
func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
|
||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
anyParams := []ExprFuncParam{
|
||||
NewFuncParam(ParamValue),
|
||||
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
||||
}
|
||||
|
||||
ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams)
|
||||
if result, ok = args[kern.ParamValue]; ok {
|
||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
||||
} else {
|
||||
err = kern.ErrWrongParamType(name, kern.ParamValue, kern.TypeAny, args[kern.ParamValue])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams)
|
||||
ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams)
|
||||
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
|
||||
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
|
||||
NewFuncParam(ParamValue),
|
||||
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
||||
// func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
// var varName string
|
||||
// var ok bool
|
||||
|
||||
// if varName, ok = args[kern.ParamName].(string); !ok {
|
||||
// return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
||||
// } else {
|
||||
// ctx.GetParent().DeleteVar(varName)
|
||||
// result = nil
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
//// import
|
||||
|
||||
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||
anyParams := []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
}
|
||||
|
||||
ctx.RegisterFunc("isNil", kern.NewGolangFunctor(isNilFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isInt", kern.NewGolangFunctor(isIntFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isFloat", kern.NewGolangFunctor(isFloatFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isBool", kern.NewGolangFunctor(isBoolFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
|
||||
|
||||
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
|
||||
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
|
||||
ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams)
|
||||
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
|
||||
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
kern.NewFuncParamFlagDef(ParamDenominator, kern.PfDefault, int64(1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("eval", kern.NewGolangFunctor(evalFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("var", NewGolangFunctor(varFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamName),
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault, nil),
|
||||
ctx.RegisterFunc("var", kern.NewGolangFunctor(varFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamName),
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault, nil),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("set", kern.NewGolangFunctor(setFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamName),
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
})
|
||||
|
||||
// ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
// kern.NewFuncParam(kern.ParamName),
|
||||
// kern.NewFuncParam(kern.ParamValue),
|
||||
// })
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
// builtin-fmt.go
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"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
|
||||
if wany, exists := ctx.GetVar(ControlStdout); exists && wany != nil {
|
||||
if wany, exists := ctx.GetVar(kern.ControlStdout); exists && wany != nil {
|
||||
w, _ = wany.(io.Writer)
|
||||
}
|
||||
if w == nil {
|
||||
@@ -21,9 +23,9 @@ func getStdout(ctx ExprContext) io.Writer {
|
||||
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
|
||||
if v, exists := args[ParamItem]; exists && v != nil {
|
||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
if v, exists := args[ParamItem]; exists && v != nil {
|
||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
n, err = fmt.Fprintln(getStdout(ctx), argv...)
|
||||
} else {
|
||||
@@ -43,12 +45,12 @@ func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any,
|
||||
return
|
||||
}
|
||||
|
||||
func ImportFmtFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
func ImportFmtFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("print", kern.NewGolangFunctor(printFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
|
||||
})
|
||||
ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
ctx.RegisterFunc("println", kern.NewGolangFunctor(printLnFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
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.
|
||||
|
||||
// builtin-import.go
|
||||
@@ -7,32 +7,35 @@ package expr
|
||||
import (
|
||||
"io"
|
||||
"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)
|
||||
}
|
||||
|
||||
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
func importAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
CtrlEnable(ctx, kern.ControlExportAll)
|
||||
return importGeneral(ctx, name, args)
|
||||
}
|
||||
|
||||
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
|
||||
if v, exists := args[ParamFilepath]; exists && v != nil {
|
||||
func importGeneral(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
dirList := buildSearchDirList(ctx, "sources", ENV_EXPR_SOURCE_PATH)
|
||||
if v, exists := args[kern.ParamFilepath]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
|
||||
}
|
||||
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 sourceFilepath string
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
if file, err = os.Open(sourceFilepath); err == nil {
|
||||
defer file.Close()
|
||||
var expr *ast
|
||||
scanner := NewScanner(file, DefaultTranslations())
|
||||
var expr *scan.Ast
|
||||
scanner := scan.NewScanner(file, scan.DefaultTranslations())
|
||||
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)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -64,12 +67,12 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
return
|
||||
}
|
||||
|
||||
func ImportImportFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamFilepath, PfRepeat),
|
||||
func ImportImportFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("import", kern.NewGolangFunctor(importFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
|
||||
})
|
||||
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamFilepath, PfRepeat),
|
||||
ctx.RegisterFunc("importAll", kern.NewGolangFunctor(importAllFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+34
-33
@@ -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.
|
||||
|
||||
// builtin-iterator.go
|
||||
@@ -7,6 +7,8 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -15,22 +17,24 @@ const (
|
||||
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
|
||||
|
||||
if it, ok = args[ParamIterator].(Iterator); !ok {
|
||||
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
|
||||
if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
|
||||
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
|
||||
return
|
||||
}
|
||||
|
||||
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
|
||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
||||
return
|
||||
if args[iterParamOperator] != nil {
|
||||
if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
|
||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var vars *DictType
|
||||
if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil {
|
||||
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], TypeName(args[iterParamVars]))
|
||||
var vars *kern.DictType
|
||||
if vars, ok = args[iterParamVars].(*kern.DictType); !ok && args[iterParamVars] != nil {
|
||||
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -46,12 +50,12 @@ func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Fu
|
||||
return
|
||||
}
|
||||
|
||||
func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var it Iterator
|
||||
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var it kern.Iterator
|
||||
var ok bool
|
||||
var op Functor
|
||||
var op kern.Functor
|
||||
var v any
|
||||
var usingDefaultOp = false
|
||||
// var usingDefaultOp = false
|
||||
var params map[string]any
|
||||
var item any
|
||||
|
||||
@@ -60,24 +64,18 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
|
||||
if it, op, err = parseRunArgs(localCtx, args); err != nil {
|
||||
return
|
||||
} else if op == nil {
|
||||
op = NewGolangFunctor(printLnFunc)
|
||||
usingDefaultOp = true
|
||||
}
|
||||
|
||||
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 v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||
break
|
||||
} else {
|
||||
var success bool
|
||||
if success, ok = ToBool(v); !success || !ok {
|
||||
if op != nil {
|
||||
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
|
||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||
break
|
||||
} else {
|
||||
var success bool
|
||||
if success, ok = kern.ToBool(v); !success || !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,16 +84,19 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
if op == nil {
|
||||
ctx.UnsafeSetVar(iterVarStatus, it.Count())
|
||||
}
|
||||
result, _ = localCtx.GetVar(iterVarStatus)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ImportIterFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamIterator),
|
||||
NewFuncParamFlag(iterParamOperator, PfOptional),
|
||||
NewFuncParamFlag(iterParamVars, PfOptional),
|
||||
func ImportIterFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("run", kern.NewGolangFunctor(runFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamIterator),
|
||||
kern.NewFuncParamFlag(iterParamOperator, kern.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.
|
||||
|
||||
// builtin-math-arith.go
|
||||
@@ -7,67 +7,69 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
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",
|
||||
funcName, paramPos+1, subPos+1, level, paramValue)
|
||||
}
|
||||
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 floatSum float64 = 0.0
|
||||
var intSum int64 = 0
|
||||
var fractSum *FractionType
|
||||
var fractSum *kern.FractionType
|
||||
var v any
|
||||
|
||||
level++
|
||||
|
||||
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)
|
||||
}
|
||||
if subIter, ok := v.(Iterator); ok {
|
||||
if subIter, ok := v.(kern.Iterator); ok {
|
||||
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
||||
break
|
||||
}
|
||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||
if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
|
||||
if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
|
||||
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
|
||||
}
|
||||
count++
|
||||
|
||||
if !sumAsFloat {
|
||||
if IsFloat(v) {
|
||||
if kern.IsFloat(v) {
|
||||
sumAsFloat = true
|
||||
if sumAsFract {
|
||||
floatSum = fractSum.toFloat()
|
||||
floatSum = fractSum.ToFloat()
|
||||
} else {
|
||||
floatSum = float64(intSum)
|
||||
}
|
||||
} else if !sumAsFract && isFraction(v) {
|
||||
fractSum = newFraction(intSum, 1)
|
||||
} else if !sumAsFract && kern.IsFraction(v) {
|
||||
fractSum = kern.NewFraction(intSum, 1)
|
||||
sumAsFract = true
|
||||
}
|
||||
}
|
||||
|
||||
if sumAsFloat {
|
||||
floatSum += numAsFloat(v)
|
||||
floatSum += kern.NumAsFloat(v)
|
||||
} else if sumAsFract {
|
||||
var item *FractionType
|
||||
var item *kern.FractionType
|
||||
var ok bool
|
||||
if item, ok = v.(*FractionType); !ok {
|
||||
if item, ok = v.(*kern.FractionType); !ok {
|
||||
iv, _ := v.(int64)
|
||||
item = newFraction(iv, 1)
|
||||
item = kern.NewFraction(iv, 1)
|
||||
}
|
||||
fractSum = sumFract(fractSum, item)
|
||||
fractSum = kern.SumFract(fractSum, item)
|
||||
} else {
|
||||
iv, _ := v.(int64)
|
||||
intSum += iv
|
||||
@@ -86,64 +88,64 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
||||
return
|
||||
}
|
||||
|
||||
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[ParamValue].([]any)
|
||||
func addFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[kern.ParamValue].([]any)
|
||||
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||
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 floatProd float64 = 1.0
|
||||
var intProd int64 = 1
|
||||
var fractProd *FractionType
|
||||
var fractProd *kern.FractionType
|
||||
var v any
|
||||
|
||||
level++
|
||||
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)
|
||||
}
|
||||
if subIter, ok := v.(Iterator); ok {
|
||||
if subIter, ok := v.(kern.Iterator); ok {
|
||||
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
||||
break
|
||||
}
|
||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||
if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
|
||||
if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
count++
|
||||
|
||||
if !mulAsFloat {
|
||||
if IsFloat(v) {
|
||||
if kern.IsFloat(v) {
|
||||
mulAsFloat = true
|
||||
if mulAsFract {
|
||||
floatProd = fractProd.toFloat()
|
||||
floatProd = fractProd.ToFloat()
|
||||
} else {
|
||||
floatProd = float64(intProd)
|
||||
}
|
||||
} else if !mulAsFract && isFraction(v) {
|
||||
fractProd = newFraction(intProd, 1)
|
||||
} else if !mulAsFract && kern.IsFraction(v) {
|
||||
fractProd = kern.NewFraction(intProd, 1)
|
||||
mulAsFract = true
|
||||
}
|
||||
}
|
||||
|
||||
if mulAsFloat {
|
||||
floatProd *= numAsFloat(v)
|
||||
floatProd *= kern.NumAsFloat(v)
|
||||
} else if mulAsFract {
|
||||
var item *FractionType
|
||||
var item *kern.FractionType
|
||||
var ok bool
|
||||
if item, ok = v.(*FractionType); !ok {
|
||||
if item, ok = v.(*kern.FractionType); !ok {
|
||||
iv, _ := v.(int64)
|
||||
item = newFraction(iv, 1)
|
||||
item = kern.NewFraction(iv, 1)
|
||||
}
|
||||
fractProd = mulFract(fractProd, item)
|
||||
fractProd = kern.MulFract(fractProd, item)
|
||||
} else {
|
||||
iv, _ := v.(int64)
|
||||
intProd *= iv
|
||||
@@ -162,19 +164,19 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
||||
return
|
||||
}
|
||||
|
||||
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[ParamValue].([]any)
|
||||
func mulFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[kern.ParamValue].([]any)
|
||||
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||
return
|
||||
}
|
||||
|
||||
func ImportMathFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
|
||||
func ImportMathFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("add", kern.NewGolangFunctor(addFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(0)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
|
||||
ctx.RegisterFunc("mul", kern.NewGolangFunctor(mulFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(1)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-os-file.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
const paramHandleOrPath = "handle-or-path"
|
||||
const fileReadTextIteratorType = "fileReadTextIterator"
|
||||
|
||||
type fileReadTextIterator struct {
|
||||
osReader *osReader
|
||||
index int64
|
||||
count int64
|
||||
line string
|
||||
autoClose bool
|
||||
}
|
||||
|
||||
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
|
||||
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) TypeName() string {
|
||||
return fileReadTextIteratorType
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) String() string {
|
||||
if it.osReader != nil && it.osReader.fh != nil {
|
||||
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
|
||||
}
|
||||
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Count() int64 {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
||||
if it.osReader.fh != nil {
|
||||
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
|
||||
it.index++
|
||||
it.count++
|
||||
item = it.line[0 : len(it.line)-1]
|
||||
} else if it.autoClose {
|
||||
it.Clean()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Current() (item any, err error) {
|
||||
if len(it.line) > 0 {
|
||||
item = it.line[0 : len(it.line)-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Index() int64 {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Reset() (err error) {
|
||||
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
|
||||
it.index = -1
|
||||
it.count = 0
|
||||
it.line = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) HasOperation(name string) bool {
|
||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Clean() (err error) {
|
||||
if it.osReader.fh != nil {
|
||||
if err = it.osReader.fh.Close(); err == nil {
|
||||
it.osReader = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) 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 fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var handle *osReader
|
||||
var invalidFileHandle any
|
||||
var ok, autoClose bool
|
||||
|
||||
result = nil
|
||||
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
|
||||
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
|
||||
var handleAny any
|
||||
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
|
||||
return
|
||||
}
|
||||
if handleAny != nil {
|
||||
handle = handleAny.(*osReader)
|
||||
autoClose = true
|
||||
}
|
||||
} else {
|
||||
invalidFileHandle = args[paramHandleOrPath]
|
||||
}
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
result = newReadTextIterator(handle, autoClose)
|
||||
|
||||
}
|
||||
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func ImportOsIterFuncs(ctx ExprContext) {
|
||||
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
||||
// NewFuncParam(paramHandleOrPath),
|
||||
// })
|
||||
// }
|
||||
|
||||
// func init() {
|
||||
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
|
||||
// }
|
||||
+43
-37
@@ -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.
|
||||
|
||||
// builtin-os-file.go
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
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) {
|
||||
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
var fh *os.File
|
||||
if fh, err = os.Create(filePath); err == nil {
|
||||
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
|
||||
}
|
||||
|
||||
func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
var fh *os.File
|
||||
if fh, err = os.Open(filePath); err == nil {
|
||||
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
|
||||
}
|
||||
|
||||
func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||
var fh *os.File
|
||||
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)}
|
||||
@@ -101,13 +103,13 @@ func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result a
|
||||
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 invalidFileHandle any
|
||||
var ok bool
|
||||
|
||||
if handle, ok = args[ParamHandle].(osHandle); !ok {
|
||||
invalidFileHandle = args[ParamHandle]
|
||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
|
||||
invalidFileHandle = args[kern.ParamHandle]
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
@@ -128,18 +130,18 @@ func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result an
|
||||
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 invalidFileHandle any
|
||||
var ok bool
|
||||
|
||||
if handle, ok = args[ParamHandle].(osHandle); !ok {
|
||||
invalidFileHandle = args[ParamHandle]
|
||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
|
||||
invalidFileHandle = args[kern.ParamHandle]
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
if w, ok := handle.(*osWriter); ok {
|
||||
if v, exists := args[ParamItem]; exists {
|
||||
if v, exists := args[kern.ParamItem]; exists {
|
||||
argv := v.([]any)
|
||||
result, err = fmt.Fprint(w.writer, argv...)
|
||||
}
|
||||
@@ -154,14 +156,14 @@ func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (resul
|
||||
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 invalidFileHandle any
|
||||
var ok bool
|
||||
|
||||
result = nil
|
||||
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
|
||||
invalidFileHandle = args[ParamHandle]
|
||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
||||
invalidFileHandle = args[kern.ParamHandle]
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
@@ -194,14 +196,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result
|
||||
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 invalidFileHandle any
|
||||
var ok bool
|
||||
|
||||
result = nil
|
||||
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
|
||||
invalidFileHandle = args[ParamHandle]
|
||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
||||
invalidFileHandle = args[kern.ParamHandle]
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
@@ -220,35 +222,39 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (res
|
||||
return
|
||||
}
|
||||
|
||||
func ImportOsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
func ImportOsFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("fileOpen", kern.NewGolangFunctor(openFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamFilepath),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
ctx.RegisterFunc("fileAppend", kern.NewGolangFunctor(appendFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamFilepath),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||
NewFuncParam(ParamFilepath),
|
||||
ctx.RegisterFunc("fileCreate", kern.NewGolangFunctor(createFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamFilepath),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(ParamHandle),
|
||||
ctx.RegisterFunc("fileClose", kern.NewGolangFunctor(closeFileFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamHandle),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParam(ParamHandle),
|
||||
NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""),
|
||||
ctx.RegisterFunc("fileWriteText", kern.NewGolangFunctor(fileWriteTextFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamHandle),
|
||||
kern.NewFuncParamFlagDef(kern.ParamItem, kern.PfDefault|kern.PfRepeat, ""),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamHandle),
|
||||
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
|
||||
ctx.RegisterFunc("fileReadText", kern.NewGolangFunctor(fileReadTextFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamHandle),
|
||||
kern.NewFuncParamFlagDef(osLimitCh, kern.PfDefault, "\n"),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamHandle),
|
||||
ctx.RegisterFunc("fileReadTextAll", kern.NewGolangFunctor(fileReadTextAllFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamHandle),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
|
||||
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.
|
||||
|
||||
// builtin-string.go
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -15,7 +17,7 @@ const (
|
||||
)
|
||||
|
||||
// --- 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 v any
|
||||
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 {
|
||||
sb.WriteString(s)
|
||||
} else {
|
||||
err = ErrExpectedGot(funcName, TypeString, v)
|
||||
err = kern.ErrExpectedGot(funcName, kern.TypeString, v)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -36,45 +38,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if sep, ok := args[ParamSeparator].(string); ok {
|
||||
if v, exists := args[ParamItem]; exists {
|
||||
func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if sep, ok := args[kern.ParamSeparator].(string); ok {
|
||||
if v, exists := args[kern.ParamItem]; exists {
|
||||
argv := v.([]any)
|
||||
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))
|
||||
} else if it, ok := argv[0].(Iterator); ok {
|
||||
} else if it, ok := argv[0].(kern.Iterator); ok {
|
||||
result, err = doJoinStr(name, sep, it)
|
||||
} else if s, ok := argv[0].(string); ok {
|
||||
result = s
|
||||
} else {
|
||||
err = ErrInvalidParameterValue(name, ParamItem, v)
|
||||
err = kern.ErrInvalidParameterValue(name, kern.ParamItem, v)
|
||||
}
|
||||
} else {
|
||||
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSeparator, kern.TypeString, args[kern.ParamSeparator])
|
||||
}
|
||||
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 count = -1
|
||||
var source string
|
||||
var ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
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
|
||||
}
|
||||
|
||||
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
|
||||
if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -90,29 +92,29 @@ func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any,
|
||||
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 ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
result = strings.TrimSpace(source)
|
||||
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 ok bool
|
||||
|
||||
result = false
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if prefix, ok = args[ParamPrefix].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
|
||||
if prefix, ok = args[kern.ParamPrefix].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamPrefix, kern.TypeString, args[kern.ParamPrefix])
|
||||
}
|
||||
if strings.HasPrefix(source, prefix) {
|
||||
result = true
|
||||
@@ -125,7 +127,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
|
||||
break
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -133,18 +135,18 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
|
||||
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 ok bool
|
||||
|
||||
result = false
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if suffix, ok = args[ParamSuffix].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
|
||||
if suffix, ok = args[kern.ParamSuffix].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSuffix, kern.TypeString, args[kern.ParamSuffix])
|
||||
}
|
||||
if strings.HasPrefix(source, suffix) {
|
||||
result = true
|
||||
@@ -157,7 +159,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
|
||||
break
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -165,24 +167,24 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
|
||||
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 count int = -1
|
||||
var parts []string
|
||||
var ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if sep, ok = args[ParamSeparator].(string); !ok {
|
||||
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
|
||||
if sep, ok = args[kern.ParamSeparator].(string); !ok {
|
||||
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)
|
||||
} 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 {
|
||||
@@ -192,7 +194,7 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
} else {
|
||||
parts = []string{}
|
||||
}
|
||||
list := make(ListType, len(parts))
|
||||
list := make(kern.ListType, len(parts))
|
||||
for i, part := range parts {
|
||||
list[i] = part
|
||||
}
|
||||
@@ -200,20 +202,20 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
return
|
||||
}
|
||||
|
||||
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
func upperStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[kern.ParamSource].(string); ok {
|
||||
result = strings.ToUpper(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
func lowerStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[kern.ParamSource].(string); ok {
|
||||
result = strings.ToLower(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -221,46 +223,46 @@ func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
// --- End of function definitions
|
||||
|
||||
// Import above functions in the context
|
||||
func ImportStringFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSeparator),
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
func ImportStringFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("strJoin", kern.NewGolangFunctor(joinStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSeparator),
|
||||
kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)),
|
||||
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
||||
ctx.RegisterFunc("strSub", kern.NewGolangFunctor(subStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParamFlagDef(kern.ParamStart, kern.PfDefault, int64(0)),
|
||||
kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""),
|
||||
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
||||
ctx.RegisterFunc("strSplit", kern.NewGolangFunctor(splitStrFunc), "list of "+kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParamFlagDef(kern.ParamSeparator, kern.PfDefault, ""),
|
||||
kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strTrim", kern.NewGolangFunctor(trimStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParam(ParamPrefix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
ctx.RegisterFunc("strStartsWith", kern.NewGolangFunctor(startsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParam(kern.ParamPrefix),
|
||||
kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParam(ParamSuffix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
ctx.RegisterFunc("strEndsWith", kern.NewGolangFunctor(endsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParam(kern.ParamSuffix),
|
||||
kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strUpper", kern.NewGolangFunctor(upperStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strLower", kern.NewGolangFunctor(lowerStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
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.
|
||||
|
||||
// builtins-register.go
|
||||
@@ -6,21 +6,23 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type builtinModule struct {
|
||||
importFunc func(ExprContext)
|
||||
importFunc func(kern.ExprContext)
|
||||
description string
|
||||
imported bool
|
||||
}
|
||||
|
||||
func newBuiltinModule(importFunc func(ExprContext), description string) *builtinModule {
|
||||
func newBuiltinModule(importFunc func(kern.ExprContext), description string) *builtinModule {
|
||||
return &builtinModule{importFunc, description, false}
|
||||
}
|
||||
|
||||
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 {
|
||||
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.
|
||||
|
||||
// data-cursors.go
|
||||
@@ -7,24 +7,26 @@ package expr
|
||||
import (
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type dataCursor struct {
|
||||
ds map[string]Functor
|
||||
ctx ExprContext
|
||||
ds map[string]kern.Functor
|
||||
ctx kern.ExprContext
|
||||
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
|
||||
index int
|
||||
count int
|
||||
index int64
|
||||
count int64
|
||||
current any
|
||||
lastErr error
|
||||
resource any
|
||||
nextFunc Functor
|
||||
cleanFunc Functor
|
||||
resetFunc Functor
|
||||
nextFunc kern.Functor
|
||||
cleanFunc kern.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{
|
||||
ds: ds,
|
||||
initState: true,
|
||||
@@ -35,14 +37,14 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
|
||||
lastErr: nil,
|
||||
resource: resource,
|
||||
ctx: ctx.Clone(),
|
||||
nextFunc: ds[NextName],
|
||||
cleanFunc: ds[CleanName],
|
||||
resetFunc: ds[ResetName],
|
||||
nextFunc: ds[kern.NextName],
|
||||
cleanFunc: ds[kern.CleanName],
|
||||
resetFunc: ds[kern.ResetName],
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *dataCursor) Context() ExprContext {
|
||||
func (dc *dataCursor) Context() kern.ExprContext {
|
||||
return dc.ctx
|
||||
}
|
||||
|
||||
@@ -79,27 +81,27 @@ func (dc *dataCursor) String() string {
|
||||
}
|
||||
|
||||
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 {
|
||||
f, ok := dc.ds[name]
|
||||
exists = ok && isFunctor(f)
|
||||
exists = ok && kern.IsFunctor(f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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())
|
||||
} else if name == CleanName {
|
||||
} else if name == kern.CleanName {
|
||||
err = dc.Clean()
|
||||
} else if name == ResetName {
|
||||
} else if name == kern.ResetName {
|
||||
err = dc.Reset()
|
||||
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
} else if functor, ok := dc.ds[name]; ok && kern.IsFunctor(functor) {
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
value, err = functor.InvokeNamed(ctx, name, args)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
} else {
|
||||
err = errNoOperation(name)
|
||||
err = kern.ErrNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -127,10 +129,10 @@ func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any
|
||||
|
||||
func (dc *dataCursor) Reset() (err error) {
|
||||
if dc.resetFunc != nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
||||
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(dc.resetFunc, []any{dc.resource})
|
||||
_, err = dc.resetFunc.InvokeNamed(ctx, kern.ResetName, actualParams)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
}
|
||||
dc.index = -1
|
||||
dc.count = 0
|
||||
@@ -142,10 +144,10 @@ func (dc *dataCursor) Reset() (err error) {
|
||||
|
||||
func (dc *dataCursor) Clean() (err error) {
|
||||
if dc.cleanFunc != nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||
_, err = dc.cleanFunc.InvokeNamed(ctx, kern.CleanName, actualParams)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
}
|
||||
dc.lastErr = io.EOF
|
||||
return
|
||||
@@ -178,13 +180,13 @@ func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at
|
||||
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 ok bool
|
||||
ctx := cloneContext(dc.ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
|
||||
actualParams := bindActualParams(filter, []any{item, dc.index})
|
||||
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil {
|
||||
actualParams := kern.BindActualParams(filter, []any{item, dc.index})
|
||||
if v, err = filter.InvokeNamed(ctx, kern.FilterName, actualParams); err == nil && v != nil {
|
||||
if accepted, ok = v.(bool); !ok {
|
||||
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
|
||||
}
|
||||
|
||||
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(mapper, []any{item, dc.index})
|
||||
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
|
||||
func (dc *dataCursor) mapItem(mapper kern.Functor, item any) (mappedItem any, err error) {
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(mapper, []any{item, dc.index})
|
||||
mappedItem, err = mapper.InvokeNamed(ctx, kern.MapName, actualParams)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -213,15 +215,15 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
||||
return
|
||||
}
|
||||
current = dc.current
|
||||
filter := dc.ds[FilterName]
|
||||
mapper := dc.ds[MapName]
|
||||
filter := dc.ds[kern.FilterName]
|
||||
mapper := dc.ds[kern.MapName]
|
||||
var item any
|
||||
for item == nil && dc.lastErr == nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
dc.index++
|
||||
|
||||
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
|
||||
actualParams := kern.BindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, kern.NextName, actualParams); dc.lastErr == nil {
|
||||
if item == nil {
|
||||
dc.lastErr = io.EOF
|
||||
} 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
|
||||
if dc.lastErr != nil {
|
||||
@@ -296,10 +298,10 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
||||
// return
|
||||
// }
|
||||
|
||||
func (dc *dataCursor) Index() int {
|
||||
func (dc *dataCursor) Index() int64 {
|
||||
return dc.index - 1
|
||||
}
|
||||
|
||||
func (dc *dataCursor) Count() int {
|
||||
func (dc *dataCursor) Count() int64 {
|
||||
return dc.count
|
||||
}
|
||||
|
||||
+70
-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.
|
||||
|
||||
// dict-iterator.go
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type dictIterMode int
|
||||
@@ -20,9 +22,9 @@ const (
|
||||
)
|
||||
|
||||
type DictIterator struct {
|
||||
a *DictType
|
||||
count int
|
||||
index int
|
||||
a *kern.DictType
|
||||
count int64
|
||||
index int64
|
||||
keys []any
|
||||
iterMode dictIterMode
|
||||
}
|
||||
@@ -65,59 +67,74 @@ 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 s string
|
||||
it = &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
|
||||
var argAny any
|
||||
|
||||
dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
|
||||
if len(args) > 0 {
|
||||
if s, err = ToGoString(args[0], "sort type"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "a", "asc":
|
||||
sortType = sortTypeAsc
|
||||
case "d", "desc":
|
||||
sortType = sortTypeDesc
|
||||
case "n", "none", "nosort", "no-sort":
|
||||
sortType = sortTypeNone
|
||||
case "", "default":
|
||||
sortType = sortTypeDefault
|
||||
default:
|
||||
err = fmt.Errorf("invalid sort type %q", s)
|
||||
argAny = args[0]
|
||||
} else {
|
||||
argAny = "default"
|
||||
}
|
||||
if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "a", "asc":
|
||||
sortType = sortTypeAsc
|
||||
case "d", "desc":
|
||||
sortType = sortTypeDesc
|
||||
case "n", "none", "nosort", "no-sort":
|
||||
sortType = sortTypeNone
|
||||
case "", "default":
|
||||
sortType = sortTypeDefault
|
||||
default:
|
||||
err = fmt.Errorf("invalid sort type %q", s)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if len(args) > 1 {
|
||||
argAny = args[1]
|
||||
} else {
|
||||
argAny = "default"
|
||||
}
|
||||
|
||||
if err == nil && len(args) > 1 {
|
||||
if s, err = ToGoString(args[1], "iteration mode"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "k", "key", "keys":
|
||||
it.iterMode = dictIterModeKeys
|
||||
case "v", "value", "values":
|
||||
it.iterMode = dictIterModeValues
|
||||
case "i", "item", "items":
|
||||
it.iterMode = dictIterModeItems
|
||||
case "", "default":
|
||||
it.iterMode = dictIterModeKeys
|
||||
default:
|
||||
err = fmt.Errorf("invalid iteration mode %q", s)
|
||||
}
|
||||
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "k", "key", "keys":
|
||||
dictIt.iterMode = dictIterModeKeys
|
||||
case "v", "value", "values":
|
||||
dictIt.iterMode = dictIterModeValues
|
||||
case "i", "item", "items":
|
||||
dictIt.iterMode = dictIterModeItems
|
||||
case "", "default":
|
||||
dictIt.iterMode = dictIterModeKeys
|
||||
default:
|
||||
err = fmt.Errorf("invalid iteration mode %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
it.makeKeys(*dict, sortType)
|
||||
|
||||
if err == nil {
|
||||
dictIt.makeKeys(*dict, sortType)
|
||||
it = dictIt
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) String() string {
|
||||
var l = 0
|
||||
var l = int64(0)
|
||||
if it.a != nil {
|
||||
l = len(*it.a)
|
||||
l = int64(len(*it.a))
|
||||
}
|
||||
return fmt.Sprintf("$(#%d)", l)
|
||||
return fmt.Sprintf("$({#%d})", l)
|
||||
}
|
||||
|
||||
func (it *DictIterator) TypeName() string {
|
||||
@@ -125,46 +142,45 @@ func (it *DictIterator) TypeName() string {
|
||||
}
|
||||
|
||||
func (it *DictIterator) HasOperation(name string) bool {
|
||||
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
|
||||
yes := slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName, KeyName, ValueName}, name)
|
||||
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, kern.KeyName, kern.ValueName}, name)
|
||||
return yes
|
||||
}
|
||||
|
||||
func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||
switch name {
|
||||
case NextName:
|
||||
case kern.NextName:
|
||||
v, err = it.Next()
|
||||
case ResetName:
|
||||
case kern.ResetName:
|
||||
err = it.Reset()
|
||||
case CleanName:
|
||||
case kern.CleanName:
|
||||
err = it.Clean()
|
||||
case IndexName:
|
||||
case kern.IndexName:
|
||||
v = int64(it.Index())
|
||||
case CurrentName:
|
||||
case kern.CurrentName:
|
||||
v, err = it.Current()
|
||||
case CountName:
|
||||
case kern.CountName:
|
||||
v = it.count
|
||||
case KeyName:
|
||||
if it.index >= 0 && it.index < len(it.keys) {
|
||||
case kern.KeyName:
|
||||
if it.index >= 0 && it.index < int64(len(it.keys)) {
|
||||
v = it.keys[it.index]
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
case ValueName:
|
||||
if it.index >= 0 && it.index < len(it.keys) {
|
||||
case kern.ValueName:
|
||||
if it.index >= 0 && it.index < int64(len(it.keys)) {
|
||||
a := *(it.a)
|
||||
v = a[it.keys[it.index]]
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
default:
|
||||
err = errNoOperation(name)
|
||||
err = kern.ErrNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
case dictIterModeKeys:
|
||||
item = it.keys[it.index]
|
||||
@@ -174,7 +190,7 @@ func (it *DictIterator) Current() (item any, err error) {
|
||||
case dictIterModeItems:
|
||||
a := *(it.a)
|
||||
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
|
||||
item = newList(pair)
|
||||
item = kern.NewList(pair)
|
||||
}
|
||||
} else {
|
||||
err = io.EOF
|
||||
@@ -190,11 +206,11 @@ func (it *DictIterator) Next() (item any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) Index() int {
|
||||
func (it *DictIterator) Index() int64 {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *DictIterator) Count() int {
|
||||
func (it *DictIterator) Count() int64 {
|
||||
return it.count
|
||||
}
|
||||
|
||||
|
||||
+346
-168
@@ -34,7 +34,7 @@ Expressions calculator
|
||||
|
||||
toc::[]
|
||||
|
||||
#TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)#
|
||||
#TODO: Work in progress (last update on 2026/04/21, 6:49 p.m.)#
|
||||
|
||||
== Expr
|
||||
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
||||
@@ -1036,13 +1036,13 @@ Builtins are collection of function dedicated to specific domains of application
|
||||
There are currently several builtin modules. More builtin modules will be added in the future.
|
||||
|
||||
.Available builtin modules
|
||||
* *base*: Base expression tools like isNil(), int(), etc.
|
||||
* *fmt*: String and console formatting functions
|
||||
* *import*: Functions import() and include()
|
||||
* *iterator*: Iterator helper functions
|
||||
* *math.arith*: Functions add() and mul()
|
||||
* *os.file*: Operating system file functions
|
||||
* *string*: string utilities
|
||||
* <<_module_base,*base*>>: Base expression tools like isNil(), int(), etc.
|
||||
* <<_module_fmt,*fmt*>>: String and console formatting functions
|
||||
* <<_module_import,*import*>>: Functions <<_import,import()>> and <<_include,include()>>
|
||||
* <<_module_iterator,*iterator*>>: Iterator helper functions
|
||||
* <<_module_math_arith,*math.arith*>>: Functions <<_add,add()>> and <<_mul,mul()>>
|
||||
* <<_module_os_file,*os.file*>>: Operating system file functions
|
||||
* <<_module_string,*string*>>: string utilities
|
||||
|
||||
Builtins activation is done by using the [blue]`BUILTIN` operator. All modules except "base" must be explicitly enabled. The syntax is as follows.
|
||||
|
||||
@@ -1087,6 +1087,8 @@ The "base" builtin module provides functions for type checking and type conversi
|
||||
|
||||
.Other functions
|
||||
* <<_eval,eval()>>
|
||||
* <<_set,set()>>
|
||||
* <<_unset,unset()>>
|
||||
* <<_var,var()>>
|
||||
|
||||
|
||||
@@ -1095,144 +1097,144 @@ Syntax: `isBool(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is boolean, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isBool(true) +
|
||||
true +
|
||||
>>> isBool(3==2) +
|
||||
true
|
||||
`>>>` [blue]`isBool(true)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isBool(3==2)` +
|
||||
[green]`true`
|
||||
|
||||
===== isDict()
|
||||
Syntax: `isDict(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is dictionary, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isDict({}) +
|
||||
true +
|
||||
>>> isDict({1: "one", 2: "two"}) +
|
||||
true +
|
||||
>>> isDict(1:"one") +
|
||||
Eval Error: denominator must be integer, got string (one)
|
||||
`>>>` [blue]`isDict({})` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isDict({1: "one", 2: "two"})` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isDict(1:"one")` +
|
||||
[red]`Eval Error: denominator must be integer, got string (one)`
|
||||
|
||||
===== isFloat()
|
||||
Syntax: `isFloat(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is float, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isFloat(4.) +
|
||||
true +
|
||||
>>> isFloat(4) +
|
||||
false +
|
||||
>>> isFloat("2.1") +
|
||||
false
|
||||
`>>>` [blue]`isFloat(4.)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isFloat(4)` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`isFloat("2.1")` +
|
||||
[green]`false`
|
||||
|
||||
===== isFract()
|
||||
Syntax: `isFract(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is fraction, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isFract(4.5) +
|
||||
false +
|
||||
>>> isFract(4:5) +
|
||||
true +
|
||||
>>> isFract(4) +
|
||||
**false** +
|
||||
>>> isFract(1.(3)) +
|
||||
true
|
||||
`>>>` [blue]`isFract(4.5)` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`isFract(4:5)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isFract(4)` +
|
||||
[green]**`false`** +
|
||||
`>>>` [blue]`isFract(1.(3))` +
|
||||
[green]`true`
|
||||
|
||||
===== isList()
|
||||
Syntax: `isList(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is list, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isList([]) +
|
||||
true +
|
||||
>>> isList([1, "2"])
|
||||
true
|
||||
>>> isList(1,2)
|
||||
Eval Error: isList(): too many params -- expected 1, got 2
|
||||
`>>>` [blue]`isList([])` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isList([1, "2"])` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isList(1,2)` +
|
||||
[red]`Eval Error: isList(): too many params -- expected 1, got 2`
|
||||
|
||||
===== isNil()
|
||||
Syntax: `isNil(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is nil, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isNil(nil)
|
||||
true
|
||||
>>> isNil(1)
|
||||
false
|
||||
`>>>` [blue]`isNil(nil)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isNil(1)` +
|
||||
[green]`false`
|
||||
|
||||
===== isRational()
|
||||
Syntax: `isRational(<expr>) -> bool` +
|
||||
Returns _true_ if the value type of _<expr>_ is fraction or int, false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isRational(4.5) +
|
||||
false +
|
||||
>>> isRational(4:5) +
|
||||
true +
|
||||
>>> isRational(4) +
|
||||
**true** +
|
||||
>>> isRational(1.(3)) +
|
||||
true
|
||||
`>>>` [blue]`isRational(4.5)` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`isRational(4:5)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isRational(4)` +
|
||||
[green]**`true`** +
|
||||
`>>>` [blue]`isRational(1.(3))` +
|
||||
[green]`true`
|
||||
|
||||
===== isString()
|
||||
Syntax: `isString(<expr>) -> bool` +
|
||||
Returns a boolean value , false otherwise.
|
||||
|
||||
.Examples
|
||||
>>> isString("ciao") +
|
||||
true +
|
||||
>>> isString(2) +
|
||||
false +
|
||||
>>> isString(2+"2") +
|
||||
true
|
||||
`>>>` [blue]`isString("ciao")` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`isString(2)` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`isString(2+"2")` +
|
||||
[green]`true`
|
||||
|
||||
===== bool()
|
||||
Syntax: `bool(<expr>) -> bool` +
|
||||
Returns a _boolean_ value consisent to the value of the expression.
|
||||
|
||||
.Examples
|
||||
>>> bool(1)
|
||||
true
|
||||
>>> bool(0)
|
||||
false
|
||||
>>> bool("")
|
||||
false
|
||||
>>> bool([])
|
||||
false
|
||||
>>> bool([1])
|
||||
true
|
||||
>>> bool({})
|
||||
false
|
||||
>>> bool({1: "one"})
|
||||
true
|
||||
`>>>` [blue]`bool(1)` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`bool(0)` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`bool("")` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`bool([])` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`bool([1])` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`bool({})` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`bool({1: "one"})` +
|
||||
[green]`true`
|
||||
|
||||
===== int()
|
||||
Syntax: `int(<expr>) -> int` +
|
||||
Returns an _integer_ value consistent with the value of the expression.
|
||||
|
||||
.Examples
|
||||
>>> int(2) +
|
||||
2 +
|
||||
>>> int("2") +
|
||||
2 +
|
||||
>>> int("0x1") +
|
||||
Eval Error: strconv.Atoi: parsing "0x1": invalid syntax +
|
||||
>>> int(0b10) +
|
||||
2 +
|
||||
>>> int(0o2) +
|
||||
2 +
|
||||
>>> int(0x2) +
|
||||
2 +
|
||||
>>> int(1.8) +
|
||||
1 +
|
||||
>>> int(5:2) +
|
||||
2 +
|
||||
>>> int([]) +
|
||||
Eval Error: int(): can't convert list to int+
|
||||
>>> int(true) +
|
||||
1 +
|
||||
>>> int(false) +
|
||||
0
|
||||
`>>>` [blue]`int(2)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int("2")` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int("0x1")` +
|
||||
[red]`Eval Error: strconv.Atoi: parsing "0x1": invalid syntax` +
|
||||
`>>>` [blue]`int(0b10)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int(0o2)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int(0x2)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int(1.8)` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`int(5:2)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`int([])` +
|
||||
[red]`Eval Error: int(): can't convert list to int` +
|
||||
`>>>` [blue]`int(true)` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`int(false)` +
|
||||
[green]`0`
|
||||
|
||||
|
||||
===== dec()
|
||||
@@ -1240,66 +1242,66 @@ Syntax: `dec(<expr>) -> float` +
|
||||
Returns a _float_ value consistent with the value of the expression.
|
||||
|
||||
.Examples
|
||||
>>> dec(2) +
|
||||
2 +
|
||||
>>> dec(2.1) +
|
||||
2.1 +
|
||||
>>> dec(2.3(1)) +
|
||||
2.311111111111111 +
|
||||
>>> dec("3.14") +
|
||||
3.14 +
|
||||
>>> dec(3:4) +
|
||||
0.75 +
|
||||
>>> dec(true) +
|
||||
1 +
|
||||
>>> dec(false) +
|
||||
0
|
||||
`>>>` [blue]`dec(2)` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`dec(2.1)` +
|
||||
[green]`2.1` +
|
||||
`>>>` [blue]`dec(2.3(1))` +
|
||||
[green]`2.311111111111111` +
|
||||
`>>>` [blue]`dec("3.14")` +
|
||||
[green]`3.14` +
|
||||
`>>>` [blue]`dec(3:4)` +
|
||||
[green]`0.75` +
|
||||
`>>>` [blue]`dec(true)` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`dec(false)` +
|
||||
[green]`0`
|
||||
|
||||
===== string()
|
||||
Syntax: `string(<expr>) -> string` +
|
||||
Returns a _string_ value consistent with the value of the expression.
|
||||
|
||||
.Examples
|
||||
>>> string(2) +
|
||||
"2" +
|
||||
>>> string(0.8) +
|
||||
"0.8" +
|
||||
>>> string([1,2]) +
|
||||
"[1, 2]" +
|
||||
>>> string({1: "one", 2: "two"}) +
|
||||
"{1: "one", 2: "two"}" +
|
||||
>>> string(2:5) +
|
||||
"2:5" +
|
||||
>>> string(3==2) +
|
||||
"false"
|
||||
`>>>` [blue]`string(2)` +
|
||||
[green]`"2"` +
|
||||
`>>>` [blue]`string(0.8)` +
|
||||
[green]`"0.8"` +
|
||||
`>>>` [blue]`string([1,2])` +
|
||||
[green]`"[1, 2]"` +
|
||||
`>>>` [blue]`string({1: "one", 2: "two"})` +
|
||||
[green]`"{1: "one", 2: "two"}"` +
|
||||
`>>>` [blue]`string(2:5)` +
|
||||
[green]`"2:5"` +
|
||||
`>>>` [blue]`string(3==2)` +
|
||||
[green]`"false"`
|
||||
|
||||
===== fract()
|
||||
Syntax: `fract(<expr>) -> fraction` +
|
||||
Returns a _fraction_ value consistent with the value of the expression.
|
||||
|
||||
.Examples
|
||||
>>> fract(2) +
|
||||
2:1 +
|
||||
>>> fract(2.5) +
|
||||
5:2 +
|
||||
>>> fract("2.5") +
|
||||
5:2 +
|
||||
>>> fract(1.(3)) +
|
||||
4:3 +
|
||||
>>> fract([2]) +
|
||||
Eval Error: fract(): can't convert list to float +
|
||||
>>> fract(false) +
|
||||
0:1 +
|
||||
>>> fract(true) +
|
||||
1:1
|
||||
`>>>` [blue]`fract(2)` +
|
||||
[green]`2:1` +
|
||||
`>>>` [blue]`fract(2.5)` +
|
||||
[green]`5:2` +
|
||||
`>>>` [blue]`fract("2.5")` +
|
||||
[green]`5:2` +
|
||||
`>>>` [blue]`fract(1.(3))` +
|
||||
[green]`4:3` +
|
||||
`>>>` [blue]`fract([2])` +
|
||||
[red]`Eval Error: fract(): can't convert list to float` +
|
||||
`>>>` [blue]`fract(false)` +
|
||||
[green]`0:1` +
|
||||
`>>>` [blue]`fract(true)` +
|
||||
[green]`1:1`
|
||||
|
||||
===== eval()
|
||||
Syntax: `eval(<string-expr>) -> any` +
|
||||
Computes and returns the value of the [.underline]#string# expression.
|
||||
|
||||
.Examples
|
||||
>>> eval( "2 + fract(1.(3))" ) +
|
||||
10:3
|
||||
`>>>` [blue]`eval( "2 + fract(1.(3))" )` +
|
||||
[green]`10:3`
|
||||
|
||||
===== var()
|
||||
Syntax: +
|
||||
@@ -1309,14 +1311,40 @@ Syntax: +
|
||||
This function allows you to define variables whose names must include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.
|
||||
|
||||
.Examples
|
||||
>>> var("$x", 3+9) +
|
||||
12 +
|
||||
>>> var("$x") +
|
||||
12 +
|
||||
>>> var("gain%", var("$x")) +
|
||||
12 +
|
||||
>>> var("gain%", var("$x")+1) +
|
||||
13
|
||||
`>>>` [blue]`var("$x", 3+9)` +
|
||||
[green]`12` +
|
||||
`>>>` [blue]`var("$x")` +
|
||||
[green]`12` +
|
||||
`>>>` [blue]`var("gain%", var("$x"))` +
|
||||
[green]`12` +
|
||||
`>>>` [blue]`var("gain%", var("$x")+1)` +
|
||||
[green]`13`
|
||||
|
||||
===== set
|
||||
Syntax: +
|
||||
`{4sp}set(<string-expr>, <expr>) -> any`
|
||||
|
||||
This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.
|
||||
|
||||
It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`set("$x", 100)` +
|
||||
[green]`100` +
|
||||
`>>>` [blue]`var("$x")` +
|
||||
[green]`100` +
|
||||
|
||||
===== unset()
|
||||
Syntax: +
|
||||
`{4sp}unset(<string-expr>) -> any`
|
||||
|
||||
This function allows you to unset a variable whose name can include special characters. The parameter is the name of the variable to unset.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`unset("$x")` +
|
||||
[green]`nil` +
|
||||
`>>>` [blue]`var("$x")` +
|
||||
[red]`Eval Error: var(): unknown variable "$x"`
|
||||
|
||||
==== Module "fmt"
|
||||
|
||||
@@ -1325,18 +1353,31 @@ This function allows you to define variables whose names must include special ch
|
||||
===== println()
|
||||
|
||||
==== Module "import"
|
||||
Module actiovation: +
|
||||
|
||||
===== _import()_
|
||||
[blue]_import([grey]#<source-file>#)_ -- loads the multi-expression contained in the specified source and returns its value.
|
||||
Syntax: +
|
||||
`{4sp}import(<source-file>)`
|
||||
|
||||
Loads the multi-expression contained in the specified source and returns its value.
|
||||
|
||||
===== _importAll()_
|
||||
|
||||
==== Module "iterator"
|
||||
|
||||
===== run()
|
||||
Syntax: +
|
||||
`{4sp}run(<iterator>, <operator>, <vars>) -> any`
|
||||
|
||||
Iterates over the specified iterator and applies the specified operator to the current value of the iterator.
|
||||
|
||||
==== Module "math.arith"
|
||||
Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.
|
||||
|
||||
* <<_add,add()>>
|
||||
* <<_mul,mul()>>
|
||||
|
||||
|
||||
===== add()
|
||||
Syntax: +
|
||||
`{4sp}add(<num-expr1>, <num-expr2>, ...) -> any` +
|
||||
@@ -1346,22 +1387,24 @@ Syntax: +
|
||||
Returns the sum of the values of the parameters. The parameters can be of any numeric type for which the [blue]`+` operator is defined. The result type depends on the types of the parameters. If all parameters are of the same type, the result is of that type. If the parameters are of different types, the result is of the type that can represent all the parameter types without loss of information. For example, if the parameters are a mix of integers and floats, the result is a float. If the parameters are a mix of number types, the result has the type of the most general one.
|
||||
|
||||
.Examples
|
||||
>>> add(1,2,3) +
|
||||
6
|
||||
>>> add(1.1,2.1,3.1) +
|
||||
6.300000000000001 +
|
||||
>>> add("1","2","3") +
|
||||
Eval Error: add(): param nr 1 (1 in 0) has wrong type string, number expected +
|
||||
>>> add(1:3, 2:3, 3:3) +
|
||||
2:1 +
|
||||
>>> add(1, "2") +
|
||||
Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected +
|
||||
>>> add([1,2,3]) +
|
||||
6 +
|
||||
>>> iterator=$([1,2,3]); add(iterator) +
|
||||
6 +
|
||||
>>> add($([1,2,3])) +
|
||||
6
|
||||
`>>>` [blue]`builtin "math.arith"` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`add(1,2,3)` +
|
||||
[green]`6` +
|
||||
`>>>` [blue]`add(1.1,2.1,3.1)` +
|
||||
[green]`6.300000000000001` +
|
||||
`>>>` [blue]`add("1","2","3")` +
|
||||
[red]`Eval Error: add(): param nr 1 (1 in 0) has wrong type string, number expected` +
|
||||
`>>>` [blue]`add(1:3, 2:3, 3:3)` +
|
||||
[green]`2:1` +
|
||||
`>>>` [blue]`add(1, "2")` +
|
||||
[red]`Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected` +
|
||||
`>>>` [blue]`add([1,2,3])` +
|
||||
[green]`6` +
|
||||
`>>>` [blue]`iterator=$([1,2,3]); add(iterator)` +
|
||||
[green]`6` +
|
||||
`>>>` [blue]`add($([1,2,3]))` +
|
||||
[green]`6`
|
||||
|
||||
===== mul()
|
||||
Syntax: +
|
||||
@@ -1369,16 +1412,47 @@ Syntax: +
|
||||
`{4sp}mul(<list-of-num-expr>]) -> any` +
|
||||
`{4sp}mul(<iterator-over-num-values>) -> any`
|
||||
|
||||
Same as add() but returns the product of the values of the parameters.
|
||||
Same as <<_add,add()>> but returns the product of the values of the parameters.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`builtin "math.arith"` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`mul(2,3,4)` +
|
||||
[green]`24`
|
||||
|
||||
==== 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()
|
||||
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()
|
||||
|
||||
===== 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()
|
||||
|
||||
@@ -1390,47 +1464,151 @@ Same as add() but returns the product of the values of the parameters.
|
||||
|
||||
|
||||
==== 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()
|
||||
Syntax: +
|
||||
`{4sp}strJoin(<separator>, <item> ...) -> string`
|
||||
|
||||
Returns a string obtained by concatenating the string items (sarting from the second argument), separated by the string value of the separator.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strJoin(", ", "one", "two", "three")` +
|
||||
[green]`"one, two, three"` +
|
||||
`>>>` [blue]`strJoin(", ", ["one", "two", "three"])` +
|
||||
[green]`"one, two, three"`
|
||||
|
||||
===== strSub()
|
||||
Syntax: +
|
||||
`{4sp}strSub(<string>, <start-index>=0, <count>=-1) -> string`
|
||||
|
||||
Returns a substring of the specified string starting at the specified index and having the specified length. If the start index is negative, it is interpreted as an offset from the end of the string. If the count is negative, it means to take all characters until the end of the string.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strSub("Hello, world!", 7, 5)` +
|
||||
[green]`"world"` +
|
||||
`>>>` [blue]`strSub("Hello, world!", -6, 5)` +
|
||||
[green]`"world"` +
|
||||
`>>>` [blue]`strSub("Hello, world!", 7)` +
|
||||
[green]`"world!"` +
|
||||
`>>>` [blue]`strSub("Hello, world!", -6)` +
|
||||
[green]`"world!"`
|
||||
|
||||
===== strSplit()
|
||||
Syntax: +
|
||||
`{4sp}strSplit(<string>, <separator>="", <count>=-1) -> list`
|
||||
|
||||
Returns a list of substrings obtained by splitting the specified string using the specified separator. If the separator is an empty string, the string is split into individual characters. If the count is negative, it means to split all occurrences of the separator.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strSplit("one, two, three", ", ")` +
|
||||
[green]`["one", "two", "three"]` +
|
||||
`>>>` [blue]`strSplit("one, two, three", ", ", 2)` +
|
||||
[green]`["one", "two, three"]` +
|
||||
`>>>` [blue]`strSplit("one, two, three")` +
|
||||
[green]`["o", "n", "e", ",", " ", "t", "w", "o", ",", " ", "t", "h", "r", "e", "e"]`
|
||||
|
||||
===== strTrim()
|
||||
Syntax: +
|
||||
`{4sp}strTrim(<string>) -> string`
|
||||
|
||||
Returns a string obtained by removing leading and trailing whitespace characters from the specified string.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strTrim(" Hello, world! ")` +
|
||||
[green]`"Hello, world!"`
|
||||
|
||||
===== strStartsWith()
|
||||
Syntax: +
|
||||
`{4sp}strStartsWith(<string>, <prefix> ...) -> bool`
|
||||
|
||||
Returns a boolean indicating whether the specified string starts with any of the given prefixes.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strStartsWith("Hello, world!", "Hello")` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`strStartsWith("Hello, world!", "world")` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`strStartsWith("Hello, world!", "Hi", "He")` +
|
||||
[green]`true`
|
||||
|
||||
|
||||
===== strEndsWith()
|
||||
Syntax: +
|
||||
`{4sp}strEndsWith(<string>, <suffix> ...) -> bool`
|
||||
|
||||
Returns a boolean indicating whether the specified string ends with any of the given suffixes.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strEndsWith("Hello, world!", "world!")` +
|
||||
[green]`true` +
|
||||
`>>>` [blue]`strEndsWith("Hello, world!", "Hello")` +
|
||||
[green]`false` +
|
||||
`>>>` [blue]`strEndsWith("Hello, world!", "Hi", "world!")` +
|
||||
[green]`true`
|
||||
|
||||
|
||||
===== strUpper()
|
||||
Syntax: +
|
||||
`{4sp}strUpper(<string>) -> string`
|
||||
|
||||
Returns a string obtained by converting all characters of the specified string to uppercase.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strUpper("Hello, world!")` +
|
||||
[green]`"HELLO, WORLD!"`
|
||||
|
||||
===== strLower()
|
||||
Syntax: +
|
||||
`{4sp}strLower(<string>) -> string`
|
||||
|
||||
Returns a string obtained by converting all characters of the specified string to lowercase.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`strLower("Hello, world!")` +
|
||||
[green]`"hello, world!"`
|
||||
|
||||
== Iterators
|
||||
Iterators are objects that can be used to traverse collections, such as lists and dictionaries. They are created by providing a _data-source_ object, the collection, in a `$(<data-source>)` expression. Once an iterator is created, it can be used to access the elements of the collection one by one.
|
||||
|
||||
In general, data-sources are objects that can be iterated over. They are defined as dictionaries having the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The _next_ function must return a special value, [blue]_nil_, when there are no more elements to iterate over.
|
||||
|
||||
Lists and, soon, dictionaries, are implicit data-sources. The syntax for creating an iterator is as follows.
|
||||
Lists and dictionaries are implicit data-sources. The syntax for creating an iterator is as follows.
|
||||
|
||||
.Iterator creation syntax
|
||||
====
|
||||
*_iterator_* = "**$(**" _data-source_ "**)**" +
|
||||
_data-source_ = _explicit_ | _list-spec_ | _dict-spec_ | _custom-data-source_
|
||||
|
||||
_explicit_ = _any-expr_ { "," _any-expr_ }
|
||||
_explicit_ = _any-expr_ { "**,**" _any-expr_ }
|
||||
|
||||
_list-spec_ = _list_ _range-options_ +
|
||||
_list_ = "**[**" _any-expr_ { "," _any-expr_ } "**]**" +
|
||||
_range-options_ = [ "," _start-index_ [ "," _end-index_ [ "," _step_ ]]] +
|
||||
_list_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" +
|
||||
_range-options_ = [ "**,**" _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]]] +
|
||||
_start-index_, _end-index_, _step_ = _integer-expr_
|
||||
|
||||
_dict-spec_ = _dict_ _dict-options_ +
|
||||
_dict_ = "**{**" _key-value-pair_ { "," _key-value-pair_ } "**}**" +
|
||||
_key-value-pair_ = _scalar-value_ ":" _any-expr_ +
|
||||
_dict_ = "**{**" _key-value-pair_ { "**,**" _key-value-pair_ } "**}**" +
|
||||
_key-value-pair_ = _scalar-value_ "**:**" _any-expr_ +
|
||||
_scalar-value_ = _string_ | _number_ | _boolean_ +
|
||||
_dict-options_ = [ "," _sort-order_ [ "," _iter-mode_ ] ] +
|
||||
_dict-options_ = [ "**,**" _sort-order_ [ "**,**" _iter-mode_ ] ] +
|
||||
_sort-order_ = _asc-order_ | _desc-order_ | _no-sort_ | _default-order_ +
|
||||
_asc-order_ = "**asc**" | "**a**" +
|
||||
_desc-order_ = "**desc**" | "**d**" +
|
||||
@@ -1473,7 +1651,7 @@ NOTE: Currently, _default-iter_ is the same as _keys-iter_. In the future, it wi
|
||||
`>>>` [blue]`it{plusplus}` +
|
||||
[red]`Eval Error: EOF`
|
||||
|
||||
On a list-type iterator creation expression, it is possible to specify an index range and a step to iterate over a subset of the list.
|
||||
When creating a list-type iterator expression, you can specify a range of indices and a step to iterate over a subset of the list.
|
||||
|
||||
Indexing starts at 0. If the start index is not specified, it defaults to 0. If the end index is not specified, it defaults to the index of the last element of the list. If the step is not specified, it defaults to 1.
|
||||
|
||||
|
||||
+292
-175
@@ -2698,25 +2698,25 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
||||
<div class="title">Available builtin modules</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>base</strong>: Base expression tools like isNil(), int(), etc.</p>
|
||||
<p><a href="#_module_base"><strong>base</strong></a>: Base expression tools like isNil(), int(), etc.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>fmt</strong>: String and console formatting functions</p>
|
||||
<p><a href="#_module_fmt"><strong>fmt</strong></a>: String and console formatting functions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>import</strong>: Functions import() and include()</p>
|
||||
<p><a href="#_module_import"><strong>import</strong></a>: Functions <a href="#_import">import()</a> and <a href="#_include">include()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>iterator</strong>: Iterator helper functions</p>
|
||||
<p><a href="#_module_iterator"><strong>iterator</strong></a>: Iterator helper functions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>math.arith</strong>: Functions add() and mul()</p>
|
||||
<p><a href="#_module_math_arith"><strong>math.arith</strong></a>: Functions <a href="#_add">add()</a> and <a href="#_mul">mul()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>os.file</strong>: Operating system file functions</p>
|
||||
<p><a href="#_module_os_file"><strong>os.file</strong></a>: Operating system file functions</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>string</strong>: string utilities</p>
|
||||
<p><a href="#_module_string"><strong>string</strong></a>: string utilities</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -2830,10 +2830,10 @@ Returns <em>true</em> if the value type of <em><expr></em> is boolean, fal
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isBool(true)<br>
|
||||
true<br>
|
||||
>>> isBool(3==2)<br>
|
||||
true</p>
|
||||
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2844,12 +2844,12 @@ Returns <em>true</em> if the value type of <em><expr></em> is dictionary,
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isDict({})<br>
|
||||
true<br>
|
||||
>>> isDict({1: "one", 2: "two"})<br>
|
||||
true<br>
|
||||
>>> isDict(1:"one")<br>
|
||||
Eval Error: denominator must be integer, got string (one)</p>
|
||||
<p><code>>>></code> <code class="blue">isDict({})</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isDict({1: "one", 2: "two"})</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isDict(1:"one")</code><br>
|
||||
<code class="red">Eval Error: denominator must be integer, got string (one)</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2860,12 +2860,12 @@ Returns <em>true</em> if the value type of <em><expr></em> is float, false
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isFloat(4.)<br>
|
||||
true<br>
|
||||
>>> isFloat(4)<br>
|
||||
false<br>
|
||||
>>> isFloat("2.1")<br>
|
||||
false</p>
|
||||
<p><code>>>></code> <code class="blue">isFloat(4.)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isFloat(4)</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">isFloat("2.1")</code><br>
|
||||
<code class="green">false</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2876,14 +2876,14 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction, fa
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isFract(4.5)<br>
|
||||
false<br>
|
||||
>>> isFract(4:5)<br>
|
||||
true<br>
|
||||
>>> isFract(4)<br>
|
||||
<strong>false</strong><br>
|
||||
>>> isFract(1.(3))<br>
|
||||
true</p>
|
||||
<p><code>>>></code> <code class="blue">isFract(4.5)</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">isFract(4:5)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isFract(4)</code><br>
|
||||
<strong class="green"><code>false</code></strong><br>
|
||||
<code>>>></code> <code class="blue">isFract(1.(3))</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2894,12 +2894,12 @@ Returns <em>true</em> if the value type of <em><expr></em> is list, false
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isList([])<br>
|
||||
true<br>
|
||||
>>> isList([1, "2"])
|
||||
true
|
||||
>>> isList(1,2)
|
||||
Eval Error: isList(): too many params — expected 1, got 2</p>
|
||||
<p><code>>>></code> <code class="blue">isList([])</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isList([1, "2"])</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isList(1,2)</code><br>
|
||||
<code class="red">Eval Error: isList(): too many params — expected 1, got 2</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2910,10 +2910,10 @@ Returns <em>true</em> if the value type of <em><expr></em> is nil, false o
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isNil(nil)
|
||||
true
|
||||
>>> isNil(1)
|
||||
false</p>
|
||||
<p><code>>>></code> <code class="blue">isNil(nil)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isNil(1)</code><br>
|
||||
<code class="green">false</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2924,14 +2924,14 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction or
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isRational(4.5)<br>
|
||||
false<br>
|
||||
>>> isRational(4:5)<br>
|
||||
true<br>
|
||||
>>> isRational(4)<br>
|
||||
<strong>true</strong><br>
|
||||
>>> isRational(1.(3))<br>
|
||||
true</p>
|
||||
<p><code>>>></code> <code class="blue">isRational(4.5)</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">isRational(4:5)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isRational(4)</code><br>
|
||||
<strong class="green"><code>true</code></strong><br>
|
||||
<code>>>></code> <code class="blue">isRational(1.(3))</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2942,12 +2942,12 @@ Returns a boolean value , false otherwise.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> isString("ciao")<br>
|
||||
true<br>
|
||||
>>> isString(2)<br>
|
||||
false<br>
|
||||
>>> isString(2+"2")<br>
|
||||
true</p>
|
||||
<p><code>>>></code> <code class="blue">isString("ciao")</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isString(2)</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">isString(2+"2")</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2958,20 +2958,20 @@ Returns a <em>boolean</em> value consisent to the value of the expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> bool(1)
|
||||
true
|
||||
>>> bool(0)
|
||||
false
|
||||
>>> bool("")
|
||||
false
|
||||
>>> bool([])
|
||||
false
|
||||
>>> bool([1])
|
||||
true
|
||||
>>> bool({})
|
||||
false
|
||||
>>> bool({1: "one"})
|
||||
true</p>
|
||||
<p><code>>>></code> <code class="blue">bool(1)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">bool(0)</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">bool("")</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">bool([])</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">bool([1])</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">bool({})</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">bool({1: "one"})</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2982,28 +2982,28 @@ Returns an <em>integer</em> value consistent with the value of the expression.</
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> int(2)<br>
|
||||
2<br>
|
||||
>>> int("2")<br>
|
||||
2<br>
|
||||
>>> int("0x1")<br>
|
||||
Eval Error: strconv.Atoi: parsing "0x1": invalid syntax<br>
|
||||
>>> int(0b10)<br>
|
||||
2<br>
|
||||
>>> int(0o2)<br>
|
||||
2<br>
|
||||
>>> int(0x2)<br>
|
||||
2<br>
|
||||
>>> int(1.8)<br>
|
||||
1<br>
|
||||
>>> int(5:2)<br>
|
||||
2<br>
|
||||
>>> int([])<br>
|
||||
Eval Error: int(): can’t convert list to int+
|
||||
>>> int(true)<br>
|
||||
1<br>
|
||||
>>> int(false)<br>
|
||||
0</p>
|
||||
<p><code>>>></code> <code class="blue">int(2)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int("2")</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int("0x1")</code><br>
|
||||
<code class="red">Eval Error: strconv.Atoi: parsing "0x1": invalid syntax</code><br>
|
||||
<code>>>></code> <code class="blue">int(0b10)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int(0o2)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int(0x2)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int(1.8)</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">int(5:2)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">int([])</code><br>
|
||||
<code class="red">Eval Error: int(): can’t convert list to int</code><br>
|
||||
<code>>>></code> <code class="blue">int(true)</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">int(false)</code><br>
|
||||
<code class="green">0</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3014,20 +3014,20 @@ Returns a <em>float</em> value consistent with the value of the expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> dec(2)<br>
|
||||
2<br>
|
||||
>>> dec(2.1)<br>
|
||||
2.1<br>
|
||||
>>> dec(2.3(1))<br>
|
||||
2.311111111111111<br>
|
||||
>>> dec("3.14")<br>
|
||||
3.14<br>
|
||||
>>> dec(3:4)<br>
|
||||
0.75<br>
|
||||
>>> dec(true)<br>
|
||||
1<br>
|
||||
>>> dec(false)<br>
|
||||
0</p>
|
||||
<p><code>>>></code> <code class="blue">dec(2)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">dec(2.1)</code><br>
|
||||
<code class="green">2.1</code><br>
|
||||
<code>>>></code> <code class="blue">dec(2.3(1))</code><br>
|
||||
<code class="green">2.311111111111111</code><br>
|
||||
<code>>>></code> <code class="blue">dec("3.14")</code><br>
|
||||
<code class="green">3.14</code><br>
|
||||
<code>>>></code> <code class="blue">dec(3:4)</code><br>
|
||||
<code class="green">0.75</code><br>
|
||||
<code>>>></code> <code class="blue">dec(true)</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">dec(false)</code><br>
|
||||
<code class="green">0</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3038,18 +3038,18 @@ Returns a <em>string</em> value consistent with the value of the expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> string(2)<br>
|
||||
"2"<br>
|
||||
>>> string(0.8)<br>
|
||||
"0.8"<br>
|
||||
>>> string([1,2])<br>
|
||||
"[1, 2]"<br>
|
||||
>>> string({1: "one", 2: "two"})<br>
|
||||
"{1: "one", 2: "two"}"<br>
|
||||
>>> string(2:5)<br>
|
||||
"2:5"<br>
|
||||
>>> string(3==2)<br>
|
||||
"false"</p>
|
||||
<p><code>>>></code> <code class="blue">string(2)</code><br>
|
||||
<code class="green">"2"</code><br>
|
||||
<code>>>></code> <code class="blue">string(0.8)</code><br>
|
||||
<code class="green">"0.8"</code><br>
|
||||
<code>>>></code> <code class="blue">string([1,2])</code><br>
|
||||
<code class="green">"[1, 2]"</code><br>
|
||||
<code>>>></code> <code class="blue">string({1: "one", 2: "two"})</code><br>
|
||||
<code class="green">"{1: "one", 2: "two"}"</code><br>
|
||||
<code>>>></code> <code class="blue">string(2:5)</code><br>
|
||||
<code class="green">"2:5"</code><br>
|
||||
<code>>>></code> <code class="blue">string(3==2)</code><br>
|
||||
<code class="green">"false"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3060,20 +3060,20 @@ Returns a <em>fraction</em> value consistent with the value of the expression.</
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> fract(2)<br>
|
||||
2:1<br>
|
||||
>>> fract(2.5)<br>
|
||||
5:2<br>
|
||||
>>> fract("2.5")<br>
|
||||
5:2<br>
|
||||
>>> fract(1.(3))<br>
|
||||
4:3<br>
|
||||
>>> fract([2])<br>
|
||||
Eval Error: fract(): can’t convert list to float<br>
|
||||
>>> fract(false)<br>
|
||||
0:1<br>
|
||||
>>> fract(true)<br>
|
||||
1:1</p>
|
||||
<p><code>>>></code> <code class="blue">fract(2)</code><br>
|
||||
<code class="green">2:1</code><br>
|
||||
<code>>>></code> <code class="blue">fract(2.5)</code><br>
|
||||
<code class="green">5:2</code><br>
|
||||
<code>>>></code> <code class="blue">fract("2.5")</code><br>
|
||||
<code class="green">5:2</code><br>
|
||||
<code>>>></code> <code class="blue">fract(1.(3))</code><br>
|
||||
<code class="green">4:3</code><br>
|
||||
<code>>>></code> <code class="blue">fract([2])</code><br>
|
||||
<code class="red">Eval Error: fract(): can’t convert list to float</code><br>
|
||||
<code>>>></code> <code class="blue">fract(false)</code><br>
|
||||
<code class="green">0:1</code><br>
|
||||
<code>>>></code> <code class="blue">fract(true)</code><br>
|
||||
<code class="green">1:1</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3084,8 +3084,8 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> eval( "2 + fract(1.(3))" )<br>
|
||||
10:3</p>
|
||||
<p><code>>>></code> <code class="blue">eval( "2 + fract(1.(3))" )</code><br>
|
||||
<code class="green">10:3</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3100,14 +3100,14 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> var("$x", 3+9)<br>
|
||||
12<br>
|
||||
>>> var("$x")<br>
|
||||
12<br>
|
||||
>>> var("gain%", var("$x"))<br>
|
||||
12<br>
|
||||
>>> var("gain%", var("$x")+1)<br>
|
||||
13</p>
|
||||
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("gain%", var("$x")+1)</code><br>
|
||||
<code class="green">13</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3160,22 +3160,24 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> add(1,2,3)<br>
|
||||
6
|
||||
>>> add(1.1,2.1,3.1)<br>
|
||||
6.300000000000001<br>
|
||||
>>> add("1","2","3")<br>
|
||||
Eval Error: add(): param nr 1 (1 in 0) has wrong type string, number expected<br>
|
||||
>>> add(1:3, 2:3, 3:3)<br>
|
||||
2:1<br>
|
||||
>>> add(1, "2")<br>
|
||||
Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected<br>
|
||||
>>> add([1,2,3])<br>
|
||||
6<br>
|
||||
>>> iterator=$([1,2,3]); add(iterator)<br>
|
||||
6<br>
|
||||
>>> add($([1,2,3]))<br>
|
||||
6</p>
|
||||
<p><code>>>></code> <code class="blue">builtin "math.arith"</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">add(1,2,3)</code><br>
|
||||
<code class="green">6</code><br>
|
||||
<code>>>></code> <code class="blue">add(1.1,2.1,3.1)</code><br>
|
||||
<code class="green">6.300000000000001</code><br>
|
||||
<code>>>></code> <code class="blue">add("1","2","3")</code><br>
|
||||
<code class="red">Eval Error: add(): param nr 1 (1 in 0) has wrong type string, number expected</code><br>
|
||||
<code>>>></code> <code class="blue">add(1:3, 2:3, 3:3)</code><br>
|
||||
<code class="green">2:1</code><br>
|
||||
<code>>>></code> <code class="blue">add(1, "2")</code><br>
|
||||
<code class="red">Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected</code><br>
|
||||
<code>>>></code> <code class="blue">add([1,2,3])</code><br>
|
||||
<code class="green">6</code><br>
|
||||
<code>>>></code> <code class="blue">iterator=$([1,2,3]); add(iterator)</code><br>
|
||||
<code class="green">6</code><br>
|
||||
<code>>>></code> <code class="blue">add($([1,2,3]))</code><br>
|
||||
<code class="green">6</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3187,7 +3189,14 @@ Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected<br
|
||||
<code>    mul(<iterator-over-num-values>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Same as add() but returns the product of the values of the parameters.</p>
|
||||
<p>Same as <a href="#_add">add()</a> but returns the product of the values of the parameters.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">builtin "math.arith"</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">mul(2,3,4)</code><br>
|
||||
<code class="green">24</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3226,35 +3235,143 @@ Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected<br
|
||||
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
||||
<div class="sect4">
|
||||
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strJoin(<separator>, <item> …​) → string</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a string obtained by concatenating the string items (sarting from the second argument), separated by the string value of the separator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strJoin(", ", "one", "two", "three")</code><br>
|
||||
<code class="green">"one, two, three"</code><br>
|
||||
<code>>>></code> <code class="blue">strJoin(", ", ["one", "two", "three"])</code><br>
|
||||
<code class="green">"one, two, three"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strsub"><a class="anchor" href="#_strsub"></a><a class="link" href="#_strsub">strSub()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strSub(<string>, <start-index>=0, <count>=-1) → string</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a substring of the specified string starting at the specified index and having the specified length. If the start index is negative, it is interpreted as an offset from the end of the string. If the count is negative, it means to take all characters until the end of the string.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strSub("Hello, world!", 7, 5)</code><br>
|
||||
<code class="green">"world"</code><br>
|
||||
<code>>>></code> <code class="blue">strSub("Hello, world!", -6, 5)</code><br>
|
||||
<code class="green">"world"</code><br>
|
||||
<code>>>></code> <code class="blue">strSub("Hello, world!", 7)</code><br>
|
||||
<code class="green">"world!"</code><br>
|
||||
<code>>>></code> <code class="blue">strSub("Hello, world!", -6)</code><br>
|
||||
<code class="green">"world!"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strsplit"><a class="anchor" href="#_strsplit"></a><a class="link" href="#_strsplit">strSplit()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strSplit(<string>, <separator>="", <count>=-1) → list</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a list of substrings obtained by splitting the specified string using the specified separator. If the separator is an empty string, the string is split into individual characters. If the count is negative, it means to split all occurrences of the separator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strSplit("one, two, three", ", ")</code><br>
|
||||
<code class="green">["one", "two", "three"]</code><br>
|
||||
<code>>>></code> <code class="blue">strSplit("one, two, three", ", ", 2)</code><br>
|
||||
<code class="green">["one", "two, three"]</code><br>
|
||||
<code>>>></code> <code class="blue">strSplit("one, two, three")</code><br>
|
||||
<code class="green">["o", "n", "e", ",", " ", "t", "w", "o", ",", " ", "t", "h", "r", "e", "e"]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strtrim"><a class="anchor" href="#_strtrim"></a><a class="link" href="#_strtrim">strTrim()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strTrim(<string>) → string</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a string obtained by removing leading and trailing whitespace characters from the specified string.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strTrim(" Hello, world! ")</code><br>
|
||||
<code class="green">"Hello, world!"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strstartswith"><a class="anchor" href="#_strstartswith"></a><a class="link" href="#_strstartswith">strStartsWith()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strStartsWith(<string>, <prefix> …​) → bool</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a boolean indicating whether the specified string starts with any of the given prefixes.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strStartsWith("Hello, world!", "Hello")</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">strStartsWith("Hello, world!", "world")</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">strStartsWith("Hello, world!", "Hi", "He")</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strendswith"><a class="anchor" href="#_strendswith"></a><a class="link" href="#_strendswith">strEndsWith()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strEndsWith(<string>, <suffix> …​) → bool</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a boolean indicating whether the specified string ends with any of the given suffixes.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strEndsWith("Hello, world!", "world!")</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">strEndsWith("Hello, world!", "Hello")</code><br>
|
||||
<code class="green">false</code><br>
|
||||
<code>>>></code> <code class="blue">strEndsWith("Hello, world!", "Hi", "world!")</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strupper"><a class="anchor" href="#_strupper"></a><a class="link" href="#_strupper">strUpper()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strUpper(<string>) → string</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a string obtained by converting all characters of the specified string to uppercase.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strUpper("Hello, world!")</code><br>
|
||||
<code class="green">"HELLO, WORLD!"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_strlower"><a class="anchor" href="#_strlower"></a><a class="link" href="#_strlower">strLower()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    strLower(<string>) → string</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a string obtained by converting all characters of the specified string to lowercase.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">strLower("Hello, world!")</code><br>
|
||||
<code class="green">"hello, world!"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -3270,7 +3387,7 @@ Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected<br
|
||||
<p>In general, data-sources are objects that can be iterated over. They are defined as dictionaries having the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The <em>next</em> function must return a special value, <em class="blue">nil</em>, when there are no more elements to iterate over.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Lists and, soon, dictionaries, are implicit data-sources. The syntax for creating an iterator is as follows.</p>
|
||||
<p>Lists and dictionaries are implicit data-sources. The syntax for creating an iterator is as follows.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 17. Iterator creation syntax</div>
|
||||
@@ -3280,20 +3397,20 @@ Eval Error: add(): param nr 2 (2 in 0) has wrong type string, number expected<br
|
||||
<em>data-source</em> = <em>explicit</em> | <em>list-spec</em> | <em>dict-spec</em> | <em>custom-data-source</em></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><em>explicit</em> = <em>any-expr</em> { "," <em>any-expr</em> }</p>
|
||||
<p><em>explicit</em> = <em>any-expr</em> { "<strong>,</strong>" <em>any-expr</em> }</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><em>list-spec</em> = <em>list</em> <em>range-options</em><br>
|
||||
<em>list</em> = "<strong>[</strong>" <em>any-expr</em> { "," <em>any-expr</em> } "<strong>]</strong>"<br>
|
||||
<em>range-options</em> = [ "," <em>start-index</em> [ "," <em>end-index</em> [ "," <em>step</em> ]]]<br>
|
||||
<em>list</em> = "<strong>[</strong>" <em>any-expr</em> { "<strong>,</strong>" <em>any-expr</em> } "<strong>]</strong>"<br>
|
||||
<em>range-options</em> = [ "<strong>,</strong>" <em>start-index</em> [ "<strong>,</strong>" <em>end-index</em> [ "<strong>,</strong>" <em>step</em> ]]]<br>
|
||||
<em>start-index</em>, <em>end-index</em>, <em>step</em> = <em>integer-expr</em></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><em>dict-spec</em> = <em>dict</em> <em>dict-options</em><br>
|
||||
<em>dict</em> = "<strong>{</strong>" <em>key-value-pair</em> { "," <em>key-value-pair</em> } "<strong>}</strong>"<br>
|
||||
<em>key-value-pair</em> = <em>scalar-value</em> ":" <em>any-expr</em><br>
|
||||
<em>dict</em> = "<strong>{</strong>" <em>key-value-pair</em> { "<strong>,</strong>" <em>key-value-pair</em> } "<strong>}</strong>"<br>
|
||||
<em>key-value-pair</em> = <em>scalar-value</em> "<strong>:</strong>" <em>any-expr</em><br>
|
||||
<em>scalar-value</em> = <em>string</em> | <em>number</em> | <em>boolean</em><br>
|
||||
<em>dict-options</em> = [ "," <em>sort-order</em> [ "," <em>iter-mode</em> ] ]<br>
|
||||
<em>dict-options</em> = [ "<strong>,</strong>" <em>sort-order</em> [ "<strong>,</strong>" <em>iter-mode</em> ] ]<br>
|
||||
<em>sort-order</em> = <em>asc-order</em> | <em>desc-order</em> | <em>no-sort</em> | <em>default-order</em><br>
|
||||
<em>asc-order</em> = "<strong>asc</strong>" | "<strong>a</strong>"<br>
|
||||
<em>desc-order</em> = "<strong>desc</strong>" | "<strong>d</strong>"<br>
|
||||
@@ -3361,7 +3478,7 @@ Currently, <em>default-iter</em> is the same as <em>keys-iter</em>. In the futur
|
||||
<code class="red">Eval Error: EOF</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>On a list-type iterator creation expression, it is possible to specify an index range and a step to iterate over a subset of the list.</p>
|
||||
<p>When creating a list-type iterator expression, you can specify a range of indices and a step to iterate over a subset of the list.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Indexing starts at 0. If the start index is not specified, it defaults to 0. If the end index is not specified, it defaults to the index of the last element of the list. If the step is not specified, it defaults to 1.</p>
|
||||
@@ -3493,7 +3610,7 @@ Iterators built on custom data-sources can provide additional named operators, d
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2026-04-19 15:03:48 +0200
|
||||
Last updated 2026-04-21 06:35:14 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
-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.
|
||||
|
||||
// global-context.go
|
||||
@@ -7,101 +7,68 @@ package expr
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
var globalCtx *SimpleStore
|
||||
//var globalCtx *SimpleStore
|
||||
|
||||
func ImportInContext(name string) (exists bool) {
|
||||
var mod *builtinModule
|
||||
if mod, exists = builtinModuleRegister[name]; exists {
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
func ImportInContext(ctx kern.ExprContext, name string) (exists bool) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
var mod *builtinModule
|
||||
if mod, exists = builtinModuleRegister[name]; exists {
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ImportInContextByGlobPattern(pattern string) (count int, err error) {
|
||||
var matched bool
|
||||
for name, mod := range builtinModuleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count int, err error) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
var matched bool
|
||||
for name, mod := range builtinModuleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetVar(ctx ExprContext, name string) (value any, exists bool) {
|
||||
if value, exists = ctx.GetVar(name); !exists {
|
||||
value, exists = globalCtx.GetVar(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)
|
||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + 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
|
||||
}
|
||||
|
||||
func GlobalCtrlGet(name string) (currentValue any) {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
}
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
return currentValue
|
||||
}
|
||||
|
||||
func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(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)
|
||||
}
|
||||
|
||||
@@ -109,11 +76,11 @@ func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
return currentStatus
|
||||
}
|
||||
|
||||
func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(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)
|
||||
}
|
||||
|
||||
@@ -121,36 +88,39 @@ func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
return currentStatus
|
||||
}
|
||||
|
||||
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
|
||||
var v any
|
||||
var exists bool
|
||||
// func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
|
||||
// var v any
|
||||
// var exists bool
|
||||
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
// if !strings.HasPrefix(name, "_") {
|
||||
// name = "_" + name
|
||||
// }
|
||||
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
v, exists = globalCtx.GetVar(name)
|
||||
}
|
||||
// if v, exists = ctx.GetVar(name); !exists {
|
||||
// v, exists = globalCtx.GetVar(name)
|
||||
// }
|
||||
|
||||
if exists {
|
||||
if b, ok := v.(bool); ok {
|
||||
status = b
|
||||
// if exists {
|
||||
// if b, ok := v.(bool); ok {
|
||||
// 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
|
||||
}
|
||||
|
||||
func getControlString(name string) (s string, exists bool) {
|
||||
var v any
|
||||
if v, exists = globalCtx.GetVar(name); exists {
|
||||
s, exists = v.(string)
|
||||
}
|
||||
func InitGlobal() (ctx kern.ExprContext) {
|
||||
ctx = NewSimpleStoreWithoutGlobalContext()
|
||||
kern.InitDefaultVars(ctx)
|
||||
ImportBuiltinsFuncs(ctx)
|
||||
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.
|
||||
|
||||
//go:build graph
|
||||
|
||||
// graph.go
|
||||
package expr
|
||||
|
||||
@@ -53,7 +55,7 @@ func (r *Reticle) computeCharWidth() {
|
||||
if v := ref.node.value(); v != nil {
|
||||
ref.label = fmt.Sprintf("%v", v)
|
||||
} 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
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
// helpers.go
|
||||
@@ -9,13 +9,17 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"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) {
|
||||
var tree *ast
|
||||
func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
|
||||
var tree *scan.Ast
|
||||
|
||||
r := strings.NewReader(source)
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
parser := NewParser()
|
||||
|
||||
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) {
|
||||
ctx := NewSimpleStore()
|
||||
ctx := NewSimpleStoreWithoutGlobalContext()
|
||||
for _, arg := range args {
|
||||
if isFunc(arg.Value) {
|
||||
if f, ok := arg.Value.(FuncTemplate); ok {
|
||||
functor := NewGolangFunctor(f)
|
||||
if util.IsFunc(arg.Value) {
|
||||
if f, ok := arg.Value.(kern.FuncTemplate); ok {
|
||||
functor := kern.NewGolangFunctor(f)
|
||||
// ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
||||
ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0),
|
||||
ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, 0),
|
||||
})
|
||||
} else {
|
||||
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)
|
||||
} else if float, ok := anyFloat(arg.Value); ok {
|
||||
} else if float, ok := kern.AnyFloat(arg.Value); ok {
|
||||
ctx.SetVar(arg.Name, float)
|
||||
} else if _, ok := arg.Value.(string); ok {
|
||||
ctx.SetVar(arg.Name, arg.Value)
|
||||
@@ -65,9 +69,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
|
||||
var tree *ast
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
|
||||
var tree *scan.Ast
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
parser := NewParser()
|
||||
|
||||
if tree, err = parser.Parse(scanner); err == nil {
|
||||
@@ -76,7 +80,7 @@ func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
|
||||
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
|
||||
if fh, err = os.Open(filePath); err != nil {
|
||||
return nil, err
|
||||
|
||||
+24
-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.
|
||||
|
||||
// import-utils.go
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,8 +22,8 @@ const (
|
||||
)
|
||||
|
||||
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
||||
if !(IsString(paramValue) /*|| isList(paramValue)*/) {
|
||||
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, TypeName(paramValue))
|
||||
if !(kern.IsString(paramValue) /*|| isList(paramValue)*/) {
|
||||
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -45,8 +48,8 @@ func addEnvImportDirs(envVarName string, dirList []string) []string {
|
||||
return dirList
|
||||
}
|
||||
|
||||
func addSearchDirs(endingPath string, dirList []string) []string {
|
||||
if dirSpec, exists := getControlString(ControlSearchPath); exists {
|
||||
func addSearchDirs(ctx kern.ExprContext, endingPath string, dirList []string) []string {
|
||||
if dirSpec, exists := getControlString(ctx, kern.ControlSearchPath); exists {
|
||||
dirs := strings.Split(dirSpec, ":")
|
||||
if dirList == nil {
|
||||
dirList = dirs
|
||||
@@ -63,9 +66,9 @@ func addSearchDirs(endingPath string, dirList []string) []string {
|
||||
return dirList
|
||||
}
|
||||
|
||||
func buildSearchDirList(endingPath, envVarName string) (dirList []string) {
|
||||
func buildSearchDirList(ctx kern.ExprContext, endingPath, envVarName string) (dirList []string) {
|
||||
dirList = addEnvImportDirs(envVarName, dirList)
|
||||
dirList = addSearchDirs(endingPath, dirList)
|
||||
dirList = addSearchDirs(ctx, endingPath, dirList)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,14 +79,26 @@ func isFile(filePath string) bool {
|
||||
|
||||
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||
var err error
|
||||
|
||||
suffix := SHAREDLIBRARY_EXTENSION
|
||||
if strings.HasSuffix(filename, ".debug") {
|
||||
suffix += ".debug"
|
||||
}
|
||||
|
||||
for _, dir := range dirList {
|
||||
if dir, err = ExpandPath(dir); err != nil {
|
||||
if dir, err = util.ExpandPath(dir); err != nil {
|
||||
continue
|
||||
}
|
||||
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
|
||||
subdir := strings.TrimSuffix(filename, suffix)
|
||||
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -94,7 +109,7 @@ func isPathRelative(filePath string) bool {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+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
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// iter-factory.go
|
||||
package expr
|
||||
|
||||
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 {
|
||||
return NewArrayIterator([]any{}), nil
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case *kern.ListType:
|
||||
it = NewListIterator(v, nil)
|
||||
case *kern.DictType:
|
||||
it, err = NewDictIterator(v, nil)
|
||||
case []any:
|
||||
it = NewArrayIterator(v)
|
||||
case kern.Iterator:
|
||||
// var exprs []*scan.Term
|
||||
it, err = NewIterIter(v, ctx, ops)
|
||||
default:
|
||||
it = NewArrayIterator([]any{value})
|
||||
}
|
||||
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.
|
||||
|
||||
// common-errors.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -34,7 +34,7 @@ func ErrCantConvert(funcName string, value any, kind string) 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 {
|
||||
@@ -52,7 +52,7 @@ func ErrFuncDivisionByZero(funcName string) 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) {
|
||||
@@ -86,6 +86,6 @@ func ErrUnknownVar(funcName, varName string) error {
|
||||
|
||||
// --- Operator errors
|
||||
|
||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
|
||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
|
||||
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.
|
||||
|
||||
// common-params.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
const (
|
||||
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.
|
||||
|
||||
// common-type-names.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
const (
|
||||
TypeAny = "any"
|
||||
@@ -13,6 +13,7 @@ const (
|
||||
TypeFileHandle = "file-handle"
|
||||
TypeInt = "integer"
|
||||
TypeItem = "item"
|
||||
TypeIterator = "iterator"
|
||||
TypeNumber = "number"
|
||||
TypePair = "pair"
|
||||
TypeString = "string"
|
||||
@@ -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.
|
||||
|
||||
// context-helpers.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
||||
func CloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
||||
if sourceCtx != nil {
|
||||
clonedCtx = sourceCtx.Clone()
|
||||
}
|
||||
@@ -25,8 +25,8 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
||||
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
|
||||
}
|
||||
|
||||
func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
||||
func ExportObjects(destCtx, sourceCtx ExprContext) {
|
||||
exportAll := CtrlIsEnabled(sourceCtx, ControlExportAll)
|
||||
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
||||
// Export variables
|
||||
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) {
|
||||
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.
|
||||
|
||||
// control.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import "strings"
|
||||
|
||||
// Preset control variables
|
||||
const (
|
||||
@@ -16,7 +18,7 @@ const (
|
||||
|
||||
// Other control variables
|
||||
const (
|
||||
control_export_all = "_export_all"
|
||||
ControlExportAll = "_export_all"
|
||||
)
|
||||
|
||||
// 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"
|
||||
)
|
||||
|
||||
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) {
|
||||
current, _ = ctx.GetVar(name)
|
||||
ctx.UnsafeSetVar(name, value)
|
||||
return
|
||||
}
|
||||
|
||||
func initDefaultVars(ctx ExprContext) {
|
||||
func InitDefaultVars(ctx ExprContext) {
|
||||
if _, exists := ctx.GetVar(ControlPreset); exists {
|
||||
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.
|
||||
|
||||
// dict-type.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,6 +12,11 @@ import (
|
||||
|
||||
type DictType map[any]any
|
||||
|
||||
func IsDict(v any) (ok bool) {
|
||||
_, ok = v.(*DictType)
|
||||
return ok
|
||||
}
|
||||
|
||||
func MakeDict() (dict *DictType) {
|
||||
d := make(DictType)
|
||||
dict = &d
|
||||
@@ -32,7 +37,7 @@ func NewDict(dictAny map[any]any) (dict *DictType) {
|
||||
return
|
||||
}
|
||||
|
||||
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||
func newDict(dictAny map[any]Term) (dict *DictType) {
|
||||
// TODO Change with a call to NewDict()
|
||||
var d DictType
|
||||
if dictAny != nil {
|
||||
@@ -110,7 +115,7 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
||||
sb.WriteString(": ")
|
||||
if formatter, ok := value.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(opt))
|
||||
} else if t, ok := value.(*term); ok {
|
||||
} else if t, ok := value.(Term); ok {
|
||||
sb.WriteString(t.String())
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%#v", value))
|
||||
@@ -129,7 +134,7 @@ func (dict *DictType) TypeName() string {
|
||||
return "dict"
|
||||
}
|
||||
|
||||
func (dict *DictType) hasKey(target any) (ok bool) {
|
||||
func (dict *DictType) HasKey(target any) (ok bool) {
|
||||
for key := range *dict {
|
||||
if ok = reflect.DeepEqual(key, target); ok {
|
||||
break
|
||||
@@ -138,7 +143,16 @@ func (dict *DictType) hasKey(target any) (ok bool) {
|
||||
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)
|
||||
for k, v := range *dict {
|
||||
(*c)[k] = v
|
||||
@@ -146,7 +160,7 @@ func (dict *DictType) clone() (c *DictType) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *DictType) merge(second *DictType) {
|
||||
func (dict *DictType) Merge(second *DictType) {
|
||||
if second != nil {
|
||||
for k, v := range *second {
|
||||
(*dict)[k] = v
|
||||
@@ -154,8 +168,21 @@ func (dict *DictType) merge(second *DictType) {
|
||||
}
|
||||
}
|
||||
|
||||
func (dict *DictType) setItem(key any, value any) (err error) {
|
||||
(*dict)[key] = value
|
||||
func (dict *DictType) Equals(dict2 DictType) (answer bool) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
// expr-context.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
// ----Expression Context
|
||||
type ExprContext interface {
|
||||
Clone() ExprContext
|
||||
SetParent(ctx ExprContext)
|
||||
GetParent() (ctx ExprContext)
|
||||
GetGlobal() (ctx ExprContext)
|
||||
GetVar(varName string) (value any, exists bool)
|
||||
GetLast() any
|
||||
SetVar(varName string, value any)
|
||||
@@ -22,6 +23,7 @@ type ExprContext interface {
|
||||
FuncCount() int
|
||||
DeleteFunc(funcName string)
|
||||
|
||||
GetLocalFuncInfo(name string) (info ExprFunc, exists bool)
|
||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||
Call(name string, args map[string]any) (result any, err error)
|
||||
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.
|
||||
|
||||
// expr-function.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
// ---- Functor interface
|
||||
type Functor interface {
|
||||
@@ -37,3 +37,8 @@ type ExprFunc interface {
|
||||
PrepareCall(name string, actualParams map[string]any) (err error)
|
||||
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.
|
||||
|
||||
// expr.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
// ----Expression 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.
|
||||
|
||||
// formatter.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import "fmt"
|
||||
|
||||
@@ -46,7 +46,7 @@ type Formatter interface {
|
||||
ToString(options FmtOpt) string
|
||||
}
|
||||
|
||||
func getFormatted(v any, opt FmtOpt) (text string) {
|
||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||
if v == nil {
|
||||
text = "(nil)"
|
||||
} 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.
|
||||
|
||||
// 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
|
||||
|
||||
@@ -18,12 +18,12 @@ type FractionType struct {
|
||||
num, den int64
|
||||
}
|
||||
|
||||
func newFraction(num, den int64) *FractionType {
|
||||
func NewFraction(num, den int64) *FractionType {
|
||||
num, den = simplifyIntegers(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
|
||||
intPart, decPart := math.Modf(f)
|
||||
if decPart < 0.0 {
|
||||
@@ -33,11 +33,11 @@ func float64ToFraction(f float64) (fract *FractionType, err error) {
|
||||
}
|
||||
dec := fmt.Sprintf("%.12f", decPart)
|
||||
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
|
||||
func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
func MakeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
var num, den int64
|
||||
var sign int64 = 1
|
||||
var parts []string
|
||||
@@ -59,7 +59,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
return
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
f = newFraction(sign*num, 1)
|
||||
f = NewFraction(sign*num, 1)
|
||||
} else if len(parts) == 2 {
|
||||
subParts := strings.SplitN(parts[1], "(", 2)
|
||||
if len(subParts) == 1 {
|
||||
@@ -76,7 +76,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
num = num*10 + int64(c-'0')
|
||||
den = den * 10
|
||||
}
|
||||
f = newFraction(sign*num, den)
|
||||
f = NewFraction(sign*num, den)
|
||||
} else if len(subParts) == 2 {
|
||||
sub := num
|
||||
mul := int64(1)
|
||||
@@ -103,7 +103,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
den *= mul
|
||||
}
|
||||
num -= sub
|
||||
f = newFraction(sign*num, den)
|
||||
f = NewFraction(sign*num, den)
|
||||
}
|
||||
}
|
||||
exit:
|
||||
@@ -113,7 +113,15 @@ exit:
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -168,7 +176,7 @@ func (f *FractionType) TypeName() string {
|
||||
// -------- fraction utility functions
|
||||
|
||||
// greatest common divider
|
||||
func gcd(a, b int64) (g int64) {
|
||||
func Gcd(a, b int64) (g int64) {
|
||||
if a < 0 {
|
||||
a = -a
|
||||
}
|
||||
@@ -189,21 +197,21 @@ func gcd(a, b int64) (g int64) {
|
||||
|
||||
// lower common multiple
|
||||
func lcm(a, b int64) (l int64) {
|
||||
g := gcd(a, b)
|
||||
g := Gcd(a, b)
|
||||
l = a * b / g
|
||||
return
|
||||
}
|
||||
|
||||
// Sum two fractions
|
||||
func sumFract(f1, f2 *FractionType) (sum *FractionType) {
|
||||
func SumFract(f1, f2 *FractionType) (sum *FractionType) {
|
||||
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
|
||||
}
|
||||
|
||||
// Multiply two fractions
|
||||
func mulFract(f1, f2 *FractionType) (prod *FractionType) {
|
||||
prod = newFraction(f1.num*f2.num, f1.den*f2.den)
|
||||
func MulFract(f1, f2 *FractionType) (prod *FractionType) {
|
||||
prod = NewFraction(f1.num*f2.num, f1.den*f2.den)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -212,11 +220,12 @@ func anyToFract(v any) (f *FractionType, err error) {
|
||||
if f, ok = v.(*FractionType); !ok {
|
||||
if n, ok := v.(int64); ok {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -230,12 +239,12 @@ func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func sumAnyFract(af1, af2 any) (sum any, err error) {
|
||||
func SumAnyFract(af1, af2 any) (sum any, err error) {
|
||||
var f1, f2 *FractionType
|
||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||
return
|
||||
}
|
||||
f := sumFract(f1, f2)
|
||||
f := SumFract(f1, f2)
|
||||
if f.num == 0 {
|
||||
sum = 0
|
||||
} else {
|
||||
@@ -250,7 +259,7 @@ func sumAnyFract(af1, af2 any) (sum any, err error) {
|
||||
// =0 if af1 == af2
|
||||
// >0 if af1 > af2
|
||||
// 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
|
||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||
return
|
||||
@@ -265,25 +274,27 @@ func cmpAnyFract(af1, af2 any) (result int, err error) {
|
||||
// =0 if af1 == af2
|
||||
// >0 if af1 > af2
|
||||
func cmpFract(f1, f2 *FractionType) (result int) {
|
||||
f2.num = -f2.num
|
||||
f := sumFract(f1, f2)
|
||||
if f.num < 0 {
|
||||
result = -1
|
||||
} else if f.num > 0 {
|
||||
result = 1
|
||||
} else {
|
||||
result = 0
|
||||
if f1 != nil && f2 != nil {
|
||||
f2.num = -f2.num
|
||||
f := SumFract(f1, f2)
|
||||
if f.num < 0 {
|
||||
result = -1
|
||||
} else if f.num > 0 {
|
||||
result = 1
|
||||
} else {
|
||||
result = 0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func subAnyFract(af1, af2 any) (sum any, err error) {
|
||||
func SubAnyFract(af1, af2 any) (sum any, err error) {
|
||||
var f1, f2 *FractionType
|
||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||
return
|
||||
}
|
||||
f2.num = -f2.num
|
||||
f := sumFract(f1, f2)
|
||||
f := SumFract(f1, f2)
|
||||
if f.num == 0 {
|
||||
sum = 0
|
||||
} else {
|
||||
@@ -292,7 +303,7 @@ func subAnyFract(af1, af2 any) (sum any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func mulAnyFract(af1, af2 any) (prod any, err error) {
|
||||
func MulAnyFract(af1, af2 any) (prod any, err error) {
|
||||
var f1, f2 *FractionType
|
||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||
return
|
||||
@@ -306,7 +317,7 @@ func mulAnyFract(af1, af2 any) (prod any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func divAnyFract(af1, af2 any) (quot any, err error) {
|
||||
func DivAnyFract(af1, af2 any) (quot any, err error) {
|
||||
var f1, f2 *FractionType
|
||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
||||
return
|
||||
@@ -345,7 +356,7 @@ func simplifyIntegers(num, den int64) (a, b int64) {
|
||||
den = -den
|
||||
num = -num
|
||||
}
|
||||
g := gcd(num, den)
|
||||
g := Gcd(num, den)
|
||||
a = num / g
|
||||
b = den / g
|
||||
return
|
||||
@@ -355,7 +366,19 @@ func intToFraction(n int64) *FractionType {
|
||||
return &FractionType{n, 1}
|
||||
}
|
||||
|
||||
func isFraction(v any) (ok bool) {
|
||||
func IsFraction(v any) (ok bool) {
|
||||
_, ok = v.(*FractionType)
|
||||
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.
|
||||
|
||||
// iterator.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import (
|
||||
// "errors"
|
||||
@@ -27,10 +27,11 @@ const (
|
||||
|
||||
type Iterator interface {
|
||||
Typer
|
||||
fmt.Stringer
|
||||
Next() (item any, err error) // must return io.EOF after the last item
|
||||
Current() (item any, err error)
|
||||
Index() int
|
||||
Count() int
|
||||
Index() int64
|
||||
Count() int64
|
||||
HasOperation(name string) bool
|
||||
CallOperation(name string, args map[string]any) (value any, err error)
|
||||
}
|
||||
@@ -41,10 +42,11 @@ type ExtIterator interface {
|
||||
Clean() error
|
||||
}
|
||||
|
||||
func errNoOperation(name string) error {
|
||||
func ErrNoOperation(name string) error {
|
||||
return fmt.Errorf("no %s() function defined in the data-source", name)
|
||||
}
|
||||
|
||||
// func errInvalidDataSource() error {
|
||||
// return errors.New("invalid data-source")
|
||||
// }
|
||||
func IsIterator(v any) (ok bool) {
|
||||
_, 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.
|
||||
|
||||
// list-type.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -12,23 +12,21 @@ import (
|
||||
|
||||
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 {
|
||||
listAny = []any{}
|
||||
}
|
||||
return newList(listAny)
|
||||
}
|
||||
|
||||
func newList(listAny []any) (list *ListType) {
|
||||
return NewList(listAny)
|
||||
}
|
||||
|
||||
func NewList(listAny []any) (list *ListType) {
|
||||
if listAny != nil {
|
||||
ls := make(ListType, len(listAny))
|
||||
// for i, item := range listAny {
|
||||
// ls[i] = item
|
||||
// }
|
||||
copy(ls, listAny)
|
||||
list = &ls
|
||||
}
|
||||
@@ -52,21 +50,21 @@ func ListFromStrings(stringList []string) (list *ListType) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
func (dict *ListType) ToString(opt FmtOpt) (s string) {
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteByte('[')
|
||||
if len(*ls) > 0 {
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
if len(*dict) > 0 {
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(nest)
|
||||
}
|
||||
for i, item := range []any(*ls) {
|
||||
for i, item := range []any(*dict) {
|
||||
if i > 0 {
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteString(",\n")
|
||||
@@ -98,30 +96,19 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *ListType) String() string {
|
||||
return ls.ToString(0)
|
||||
func (dict *ListType) String() string {
|
||||
return dict.ToString(0)
|
||||
}
|
||||
|
||||
func (ls *ListType) TypeName() string {
|
||||
func (dict *ListType) TypeName() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
// func (list *ListType) indexDeepCmp(target any) (index int) {
|
||||
// index = -1
|
||||
// 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) {
|
||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*dict) >= len(*t) {
|
||||
answer = true
|
||||
for _, item := range *t {
|
||||
if answer = ls.indexDeepSameCmp(item) >= 0; !answer {
|
||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -129,12 +116,29 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
||||
answer = true
|
||||
for index, i1 := range *ls1 {
|
||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
||||
// answer = false
|
||||
// break
|
||||
// }
|
||||
if !Equal(i1, ls2[index]) {
|
||||
answer = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
var eq bool
|
||||
var err error
|
||||
index = -1
|
||||
for i, item := range *list {
|
||||
if eq, err = deepSame(item, target, sameContent); err != nil {
|
||||
for i, item := range *dict {
|
||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
||||
break
|
||||
} else if eq {
|
||||
index = i
|
||||
@@ -144,13 +148,13 @@ func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||
return
|
||||
}
|
||||
|
||||
func sameContent(a, b any) (same bool, err error) {
|
||||
func SameContent(a, b any) (same bool, err error) {
|
||||
la, _ := a.(*ListType)
|
||||
lb, _ := b.(*ListType)
|
||||
if len(*la) == len(*lb) {
|
||||
same = true
|
||||
for _, item := range *la {
|
||||
if pos := lb.indexDeepSameCmp(item); pos < 0 {
|
||||
if pos := lb.IndexDeepSameCmp(item); pos < 0 {
|
||||
same = false
|
||||
break
|
||||
}
|
||||
@@ -159,19 +163,19 @@ func sameContent(a, b any) (same bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
||||
if isNumOrFract(a) && isNumOrFract(b) {
|
||||
func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
|
||||
if IsNumOrFract(a) && IsNumOrFract(b) {
|
||||
if IsNumber(a) && IsNumber(b) {
|
||||
if IsInteger(a) && IsInteger(b) {
|
||||
li, _ := a.(int64)
|
||||
ri, _ := b.(int64)
|
||||
eq = li == ri
|
||||
} else {
|
||||
eq = numAsFloat(a) == numAsFloat(b)
|
||||
eq = NumAsFloat(a) == NumAsFloat(b)
|
||||
}
|
||||
} else {
|
||||
var cmp int
|
||||
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||
if cmp, err = CmpAnyFract(a, b); err == nil {
|
||||
eq = cmp == 0
|
||||
}
|
||||
}
|
||||
@@ -184,12 +188,15 @@ func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) setItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*list)) {
|
||||
(*list)[index] = value
|
||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*dict)) {
|
||||
(*dict)[index] = value
|
||||
} 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
|
||||
}
|
||||
|
||||
func (dict *ListType) AppendItem(value any) {
|
||||
*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)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build darwin
|
||||
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-darwin.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".dylib"
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build linux
|
||||
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-linux.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".so"
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build windows
|
||||
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-windows.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".dll"
|
||||
+32
-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.
|
||||
|
||||
// list-iterator.go
|
||||
@@ -7,40 +7,43 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type ListIterator struct {
|
||||
a *ListType
|
||||
count int
|
||||
index int
|
||||
start int
|
||||
stop int
|
||||
step int
|
||||
a *kern.ListType
|
||||
count int64
|
||||
index int64
|
||||
start int64
|
||||
stop int64
|
||||
step int64
|
||||
}
|
||||
|
||||
func NewListIterator(list *ListType, args []any) (it *ListIterator) {
|
||||
func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
|
||||
var argc int = 0
|
||||
listLen := len(([]any)(*list))
|
||||
listLen := int64(len(([]any)(*list)))
|
||||
if args != nil {
|
||||
argc = len(args)
|
||||
}
|
||||
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 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 {
|
||||
i = listLen + i
|
||||
}
|
||||
it.start = i
|
||||
}
|
||||
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 {
|
||||
i = listLen + i
|
||||
}
|
||||
it.stop = i
|
||||
}
|
||||
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 {
|
||||
i = -i
|
||||
}
|
||||
@@ -58,31 +61,16 @@ func NewListIterator(list *ListType, args []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}
|
||||
return
|
||||
}
|
||||
|
||||
func NewAnyIterator(value any) (it *ListIterator) {
|
||||
if value == nil {
|
||||
it = NewArrayIterator([]any{})
|
||||
} else if list, ok := value.(*ListType); ok {
|
||||
it = NewListIterator(list, nil)
|
||||
} else if array, ok := value.([]any); ok {
|
||||
it = NewArrayIterator(array)
|
||||
} else if it1, ok := value.(*ListIterator); ok {
|
||||
it = it1
|
||||
} else {
|
||||
it = NewArrayIterator([]any{value})
|
||||
}
|
||||
it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *ListIterator) String() string {
|
||||
var l = 0
|
||||
var l = int64(0)
|
||||
if it.a != nil {
|
||||
l = len(*it.a)
|
||||
l = int64(len(*it.a))
|
||||
}
|
||||
return fmt.Sprintf("$(#%d)", l)
|
||||
return fmt.Sprintf("$([#%d])", l)
|
||||
}
|
||||
|
||||
func (it *ListIterator) TypeName() string {
|
||||
@@ -90,26 +78,27 @@ func (it *ListIterator) TypeName() string {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||
switch name {
|
||||
case NextName:
|
||||
case kern.NextName:
|
||||
v, err = it.Next()
|
||||
case ResetName:
|
||||
case kern.ResetName:
|
||||
err = it.Reset()
|
||||
case CleanName:
|
||||
case kern.CleanName:
|
||||
err = it.Clean()
|
||||
case IndexName:
|
||||
case kern.IndexName:
|
||||
v = int64(it.Index())
|
||||
case CurrentName:
|
||||
case kern.CurrentName:
|
||||
v, err = it.Current()
|
||||
case CountName:
|
||||
case kern.CountName:
|
||||
v = it.count
|
||||
default:
|
||||
err = errNoOperation(name)
|
||||
err = kern.ErrNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -117,13 +106,13 @@ func (it *ListIterator) CallOperation(name string, args map[string]any) (v any,
|
||||
func (it *ListIterator) Current() (item any, err error) {
|
||||
a := *(it.a)
|
||||
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]
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
} 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]
|
||||
} else {
|
||||
err = io.EOF
|
||||
@@ -141,11 +130,11 @@ func (it *ListIterator) Next() (item any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (it *ListIterator) Index() int {
|
||||
func (it *ListIterator) Index() int64 {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *ListIterator) Count() int {
|
||||
func (it *ListIterator) Count() int64 {
|
||||
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.
|
||||
|
||||
// operand-dict.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- dict term
|
||||
func newDictTerm(args map[any]*term) *term {
|
||||
return &term{
|
||||
tk: *NewValueToken(0, 0, SymDict, "{}", args),
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalDict,
|
||||
func newDictTerm(args map[any]*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *scan.NewValueToken(0, 0, scan.SymDict, "{}", args),
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalDict,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- dict func
|
||||
func evalDict(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
dict, _ := opTerm.value().(map[any]*term)
|
||||
items := make(DictType, len(dict))
|
||||
func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
dict, _ := opTerm.Value().(map[any]*scan.Term)
|
||||
items := make(kern.DictType, len(dict))
|
||||
for key, tree := range dict {
|
||||
var param any
|
||||
if param, err = tree.compute(ctx); err != nil {
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
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.
|
||||
|
||||
// operand-expr.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- expr term
|
||||
func newExprTerm(root *term) *term {
|
||||
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.source(), root)
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalExpr,
|
||||
func newExprTerm(root *scan.Term) *scan.Term {
|
||||
tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymExpression, root.Source(), root)
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalExpr,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval expr
|
||||
func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if expr, ok := opTerm.value().(*term); ok {
|
||||
v, err = expr.compute(ctx)
|
||||
func evalExpr(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if ast, ok := opTerm.Value().(*scan.Term); ok {
|
||||
v, err = ast.Compute(ctx)
|
||||
} else {
|
||||
err = fmt.Errorf("expression expected, got %T", opTerm.value())
|
||||
err = fmt.Errorf("expression expected, got %T", opTerm.Value())
|
||||
}
|
||||
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.
|
||||
|
||||
// operand-func.go
|
||||
@@ -6,27 +6,34 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- function call term
|
||||
func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: args,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalFuncCall,
|
||||
func newFuncCallTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
var pos scan.TermPosition = scan.PosLeaf
|
||||
if len(args) > 0 {
|
||||
pos = scan.PosMultifix
|
||||
}
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: args,
|
||||
Position: pos,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalFuncCall,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func call
|
||||
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
// name, _ := opTerm.tk.Value.(string)
|
||||
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
|
||||
// for i, tree := range opTerm.children {
|
||||
// name, _ := opTerm.Tk.Value.(string)
|
||||
// params := make([]any, len(opTerm.Children), len(opTerm.Children)+5)
|
||||
// for i, tree := range opTerm.Children {
|
||||
// var param any
|
||||
// if param, err = tree.compute(ctx); err != nil {
|
||||
// if param, err = tree.Compute(ctx); err != nil {
|
||||
// break
|
||||
// }
|
||||
// params[i] = param
|
||||
@@ -38,42 +45,42 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
// return
|
||||
// }
|
||||
|
||||
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
name, _ := opTerm.tk.Value.(string)
|
||||
v, err = CallFunctionByTerm(ctx, name, opTerm)
|
||||
func evalFuncCall(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
name, _ := opTerm.Tk.Value.(string)
|
||||
v, err = kern.CallFunctionByTerm(ctx, name, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
// -------- function definition term
|
||||
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||
return &term{
|
||||
tk: *tk, // value is the expression body
|
||||
parent: nil,
|
||||
children: args, // function params
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalFuncDef,
|
||||
func newFuncDefTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk, // value is the expression body
|
||||
Parent: nil,
|
||||
Children: args, // function params
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalFuncDef,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func def
|
||||
func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
bodySpec := opTerm.value()
|
||||
if expr, ok := bodySpec.(*ast); ok {
|
||||
paramList := make([]ExprFuncParam, 0, len(opTerm.children))
|
||||
for _, param := range opTerm.children {
|
||||
func evalFuncDef(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
bodySpec := opTerm.Value()
|
||||
if ast, ok := bodySpec.(*scan.Ast); ok {
|
||||
paramList := make([]kern.ExprFuncParam, 0, len(opTerm.Children))
|
||||
for _, param := range opTerm.Children {
|
||||
var defValue any
|
||||
flags := paramFlags(0)
|
||||
if len(param.children) > 0 {
|
||||
flags |= PfDefault
|
||||
if defValue, err = param.children[0].compute(ctx); err != nil {
|
||||
flags := kern.FuncParamFlags(0)
|
||||
if len(param.Children) > 0 {
|
||||
flags |= kern.PfDefault
|
||||
if defValue, err = param.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
info := NewFuncParamFlagDef(param.source(), flags, defValue)
|
||||
info := kern.NewFuncParamFlagDef(param.Source(), flags, defValue)
|
||||
paramList = append(paramList, info)
|
||||
}
|
||||
v = newExprFunctor(expr, paramList, ctx)
|
||||
v = newExprFunctor(ast, paramList, ctx)
|
||||
} else {
|
||||
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.
|
||||
|
||||
// operand-iterator.go
|
||||
@@ -7,29 +7,32 @@ package expr
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- iterator term
|
||||
|
||||
func newIteratorTerm(tk *Token, args []*term) *term {
|
||||
tk.Sym = SymIterator
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: args,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalIterator,
|
||||
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
tk.Sym = scan.SymIterator
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: args,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalIterator,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 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))
|
||||
for i, t := range terms {
|
||||
var value any
|
||||
if value, err = t.compute(ctx); err == nil {
|
||||
if value, err = t.Compute(ctx); err == nil {
|
||||
values[i] = value
|
||||
} else {
|
||||
break
|
||||
@@ -38,25 +41,25 @@ func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) {
|
||||
if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
|
||||
func evalFirstChild(ctx kern.ExprContext, iteratorTerm *scan.Term) (value any, err error) {
|
||||
if len(iteratorTerm.Children) < 1 || iteratorTerm.Children[0] == nil {
|
||||
err = iteratorTerm.Errorf("missing the data-source parameter")
|
||||
return
|
||||
}
|
||||
|
||||
value, err = iteratorTerm.children[0].compute(ctx)
|
||||
value, err = iteratorTerm.Children[0].Compute(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
|
||||
if dictAny, ok := firstChildValue.(*DictType); ok {
|
||||
requiredFields := []string{NextName}
|
||||
func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
|
||||
if dictAny, ok := firstChildValue.(*kern.DictType); ok {
|
||||
requiredFields := []string{kern.NextName}
|
||||
fieldsMask := 0b1
|
||||
foundFields := 0
|
||||
ds = make(map[string]Functor)
|
||||
ds = make(map[string]kern.Functor)
|
||||
for keyAny, item := range *dictAny {
|
||||
if key, ok := keyAny.(string); ok {
|
||||
if functor, ok := item.(Functor); ok {
|
||||
if functor, ok := item.(kern.Functor); ok {
|
||||
ds[key] = functor
|
||||
if index := slices.Index(requiredFields, key); index >= 0 {
|
||||
foundFields |= 1 << index
|
||||
@@ -72,15 +75,15 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
|
||||
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
|
||||
}
|
||||
|
||||
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 ds map[string]Functor
|
||||
var ds map[string]kern.Functor
|
||||
|
||||
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
|
||||
return
|
||||
@@ -95,24 +98,24 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if len(ds) > 0 {
|
||||
var dc *dataCursor
|
||||
dcCtx := ctx.Clone()
|
||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||
if initFunc, exists := ds[kern.InitName]; exists && initFunc != nil {
|
||||
var args []any
|
||||
var resource any
|
||||
if len(opTerm.children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
||||
if len(opTerm.Children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.Children[1:]); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
args = []any{}
|
||||
}
|
||||
|
||||
actualParams := bindActualParams(initFunc, args)
|
||||
actualParams := kern.BindActualParams(initFunc, args)
|
||||
|
||||
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
|
||||
}
|
||||
exportObjects(dcCtx, initCtx)
|
||||
kern.ExportObjects(dcCtx, initCtx)
|
||||
dc = NewDataCursor(dcCtx, ds, resource)
|
||||
} else {
|
||||
dc = NewDataCursor(dcCtx, ds, nil)
|
||||
@@ -120,30 +123,66 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
v = dc
|
||||
} else {
|
||||
if dictIt, ok := firstChildValue.(*DictType); ok {
|
||||
if dictIt, ok := firstChildValue.(*kern.DictType); ok {
|
||||
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)
|
||||
}
|
||||
} 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
|
||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||
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 {
|
||||
var list []any
|
||||
if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
|
||||
v = NewArrayIterator(list)
|
||||
var siblings []any
|
||||
if siblings, err = evalSiblings(ctx, opTerm.Children, firstChildValue); err == nil {
|
||||
v = NewArrayIterator(siblings)
|
||||
}
|
||||
}
|
||||
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))
|
||||
for i, tree := range terms {
|
||||
var param any
|
||||
@@ -152,7 +191,7 @@ func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []an
|
||||
continue
|
||||
}
|
||||
param = firstChildValue
|
||||
} else if param, err = tree.compute(ctx); err != nil {
|
||||
} else if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
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.
|
||||
|
||||
// operand-list.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- list term
|
||||
func newListTermA(args ...*term) *term {
|
||||
func newListTermA(args ...*scan.Term) *scan.Term {
|
||||
return newListTerm(0, 0, args)
|
||||
}
|
||||
|
||||
func newListTerm(row, col int, args []*term) *term {
|
||||
return &term{
|
||||
tk: *NewValueToken(row, col, SymList, "[]", args),
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalList,
|
||||
func newListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *scan.NewValueToken(row, col, scan.SymList, "[]", args),
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalList,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- list func
|
||||
func evalList(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
list, _ := opTerm.value().([]*term)
|
||||
items := make(ListType, len(list))
|
||||
func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
list, _ := opTerm.Value().([]*scan.Term)
|
||||
items := make(kern.ListType, len(list))
|
||||
for i, tree := range list {
|
||||
var param any
|
||||
if param, err = tree.compute(ctx); err != nil {
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
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.
|
||||
|
||||
// operand-literal.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- literal term
|
||||
func newLiteralTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalLiteral,
|
||||
func newLiteralTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalLiteral,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func
|
||||
func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
v = opTerm.tk.Value
|
||||
func evalLiteral(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v = opTerm.Tk.Value
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymString, newLiteralTerm)
|
||||
registerTermConstructor(SymInteger, newLiteralTerm)
|
||||
registerTermConstructor(SymFloat, newLiteralTerm)
|
||||
registerTermConstructor(SymFraction, newLiteralTerm)
|
||||
registerTermConstructor(SymBool, newLiteralTerm)
|
||||
registerTermConstructor(SymKwNil, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymString, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymInteger, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymFloat, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymFraction, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymBool, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymKwNil, newLiteralTerm)
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
// operand-selector-case.go
|
||||
@@ -7,19 +7,22 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- selector case term
|
||||
|
||||
type selectorCase struct {
|
||||
filterList *term
|
||||
caseExpr Expr
|
||||
filterList *scan.Term
|
||||
caseExpr kern.Expr
|
||||
}
|
||||
|
||||
func (sc *selectorCase) String() string {
|
||||
var sb strings.Builder
|
||||
if sc.filterList != nil {
|
||||
sc.filterList.toString(&sb)
|
||||
sc.filterList.ToString(&sb)
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
sb.WriteByte('{')
|
||||
@@ -28,23 +31,23 @@ func (sc *selectorCase) String() string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
|
||||
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalSelectorCase,
|
||||
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
|
||||
tk := scan.NewValueToken(row, col, scan.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalSelectorCase,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 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
|
||||
if v, ok = opTerm.value().(*selectorCase); !ok {
|
||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
|
||||
if v, ok = opTerm.Value().(*selectorCase); !ok {
|
||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
|
||||
}
|
||||
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.
|
||||
|
||||
// operand-var.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- variable term
|
||||
func newVarTerm(tk *Token) *term {
|
||||
t := &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalVar,
|
||||
func newVarTerm(tk *scan.Token) *scan.Term {
|
||||
t := &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalVar,
|
||||
}
|
||||
t.tk.Sym = SymVariable
|
||||
t.Tk.Sym = scan.SymVariable
|
||||
return t
|
||||
}
|
||||
|
||||
// -------- 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
|
||||
name := opTerm.source()
|
||||
if v, exists = GetVar(ctx, name); !exists {
|
||||
if info, exists := GetFuncInfo(ctx, name); exists {
|
||||
name := opTerm.Source()
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
v = info.Functor()
|
||||
} else {
|
||||
err = fmt.Errorf("undefined variable or function %q", name)
|
||||
@@ -36,5 +41,5 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-assign.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
)
|
||||
|
||||
//-------- assign term
|
||||
|
||||
func newAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalAssign,
|
||||
func newAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAssign,
|
||||
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 keyList *ListType
|
||||
var keyList *kern.ListType
|
||||
var ok bool
|
||||
|
||||
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
||||
return
|
||||
}
|
||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||
@@ -37,54 +43,54 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
|
||||
}
|
||||
|
||||
switch collection := collectionValue.(type) {
|
||||
case *ListType:
|
||||
case *kern.ListType:
|
||||
if index, ok := keyValue.(int64); ok {
|
||||
err = collection.setItem(index, value)
|
||||
err = collection.SetItem(index, value)
|
||||
} 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:
|
||||
err = collection.setItem(keyValue, value)
|
||||
case *kern.DictType:
|
||||
collection.SetItem(keyValue, value)
|
||||
default:
|
||||
err = collectionTerm.Errorf("collection expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) {
|
||||
if leftTerm.symbol() == SymIndex {
|
||||
err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v)
|
||||
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) {
|
||||
if leftTerm.Symbol() == scan.SymIndex {
|
||||
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
||||
} else {
|
||||
ctx.UnsafeSetVar(leftTerm.source(), v)
|
||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
leftSym := leftTerm.symbol()
|
||||
if leftSym != SymVariable && leftSym != SymIndex {
|
||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||
leftTerm := opTerm.Children[0]
|
||||
leftSym := leftTerm.Symbol()
|
||||
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())
|
||||
return
|
||||
}
|
||||
|
||||
rightChild := opTerm.children[1]
|
||||
rightChild := opTerm.Children[1]
|
||||
|
||||
if v, err = rightChild.compute(ctx); err == nil {
|
||||
if functor, ok := v.(Functor); ok {
|
||||
if leftSym == SymVariable {
|
||||
if v, err = rightChild.Compute(ctx); err == nil {
|
||||
if functor, ok := v.(kern.Functor); ok {
|
||||
if leftSym == scan.SymVariable {
|
||||
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 {
|
||||
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 {
|
||||
err = opTerm.Errorf("unknown function %s()", rightChild.source())
|
||||
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
||||
}
|
||||
} else {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
@@ -101,29 +107,29 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- assign term
|
||||
|
||||
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalOpAssign,
|
||||
func newOpAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAssign,
|
||||
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 keyList *ListType
|
||||
var keyList *kern.ListType
|
||||
var ok bool
|
||||
|
||||
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
||||
return
|
||||
}
|
||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||
@@ -132,13 +138,13 @@ func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term)
|
||||
}
|
||||
|
||||
switch collection := collectionValue.(type) {
|
||||
case *ListType:
|
||||
case *kern.ListType:
|
||||
if index, ok := keyValue.(int64); ok {
|
||||
value = (*collection)[index]
|
||||
} 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]
|
||||
default:
|
||||
err = collectionTerm.Errorf("collection expected")
|
||||
@@ -146,55 +152,55 @@ func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term)
|
||||
return
|
||||
}
|
||||
|
||||
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||
if leftTerm.symbol() == SymIndex {
|
||||
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||
func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) {
|
||||
if leftTerm.Symbol() == scan.SymIndex {
|
||||
value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1])
|
||||
} else {
|
||||
value, _ = ctx.GetVar(leftTerm.source())
|
||||
value, _ = ctx.GetVar(leftTerm.Source())
|
||||
}
|
||||
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
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
leftSym := leftTerm.symbol()
|
||||
if leftSym != SymVariable && leftSym != SymIndex {
|
||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||
leftTerm := opTerm.Children[0]
|
||||
leftSym := leftTerm.Symbol()
|
||||
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())
|
||||
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 {
|
||||
switch opTerm.symbol() {
|
||||
case SymPlusEqual:
|
||||
switch opTerm.Symbol() {
|
||||
case scan.SymPlusEqual:
|
||||
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||
case SymMinusEqual:
|
||||
case scan.SymMinusEqual:
|
||||
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||
case SymStarEqual:
|
||||
case scan.SymStarEqual:
|
||||
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||
case SymSlashEqual:
|
||||
case scan.SymSlashEqual:
|
||||
v, err = divValues(opTerm, leftValue, rightValue)
|
||||
case SymPercEqual:
|
||||
case scan.SymPercEqual:
|
||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||
case SymAmpersandEqual:
|
||||
case scan.SymAmpersandEqual:
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
case SymVertBarEqual:
|
||||
case scan.SymVertBarEqual:
|
||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||
case SymCaretEqual:
|
||||
case scan.SymCaretEqual:
|
||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||
case SymDoubleLessEqual:
|
||||
case scan.SymDoubleLessEqual:
|
||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||
case SymDoubleGreaterEqual:
|
||||
case scan.SymDoubleGreaterEqual:
|
||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||
default:
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
||||
}
|
||||
if err == nil {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
@@ -206,15 +212,15 @@ func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymEqual, newAssignTerm)
|
||||
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm)
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
// operator-bitwise.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- Bitwise NOT term
|
||||
|
||||
func newBitwiseNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBitwiseNot,
|
||||
evalFunc: evalBitwiseNot,
|
||||
func newBitwiseNotTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriBitwiseNot,
|
||||
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
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if value, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
if kern.IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise AND term
|
||||
|
||||
func newBitwiseAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseAnd,
|
||||
evalFunc: evalBitwiseAnd,
|
||||
func newBitwiseAndTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseAnd,
|
||||
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 lok, rok bool
|
||||
|
||||
@@ -54,15 +59,15 @@ func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
@@ -71,17 +76,17 @@ func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- Bitwise OR term
|
||||
|
||||
func newBitwiseOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseOr,
|
||||
func newBitwiseOrTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseOr,
|
||||
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 lok, rok bool
|
||||
|
||||
@@ -91,15 +96,15 @@ func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,17 +114,17 @@ func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- Bitwise XOR term
|
||||
|
||||
func newBitwiseXorTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseXor,
|
||||
func newBitwiseXorTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseXor,
|
||||
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 lok, rok bool
|
||||
|
||||
@@ -129,15 +134,15 @@ func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt ^ rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -147,8 +152,8 @@ func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
||||
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
||||
scan.RegisterTermConstructor(scan.SymTilde, newBitwiseNotTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAmpersand, newBitwiseAndTerm)
|
||||
scan.RegisterTermConstructor(scan.SymVertBar, newBitwiseOrTerm)
|
||||
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.
|
||||
|
||||
// operator-bool.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- NOT term
|
||||
|
||||
func newNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priNot,
|
||||
evalFunc: evalNot,
|
||||
func newNotTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriNot,
|
||||
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
|
||||
|
||||
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if rightValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if b, ok := ToBool(rightValue); ok {
|
||||
if b, ok := kern.ToBool(rightValue); ok {
|
||||
v = !b
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(rightValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- AND term
|
||||
|
||||
func newAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAnd,
|
||||
evalFunc: evalAnd,
|
||||
func newAndTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAnd,
|
||||
EvalFunc: evalAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
|
||||
func evalAnd(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
||||
v, err = evalAndWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalAndWithoutShortcut(ctx, self)
|
||||
@@ -54,47 +59,48 @@ func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
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 leftBool, rightBool bool
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftBool, lok = ToBool(leftValue)
|
||||
rightBool, rok = ToBool(rightValue)
|
||||
leftBool, lok = kern.ToBool(leftValue)
|
||||
rightBool, rok = kern.ToBool(rightValue)
|
||||
|
||||
if lok && rok {
|
||||
v = leftBool && rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if err = self.checkOperands(); err != nil {
|
||||
if err = self.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||
if leftValue, err = self.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftBool, lok := ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue))
|
||||
return
|
||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
||||
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
|
||||
// return
|
||||
err = self.ErrIncompatibleType(leftValue, "left")
|
||||
} else if !leftBool {
|
||||
v = false
|
||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||
if rightBool, rok := ToBool(rightValue); rok {
|
||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
||||
v = rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -102,18 +108,18 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
|
||||
//-------- OR term
|
||||
|
||||
func newOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priOr,
|
||||
evalFunc: evalOr,
|
||||
func newOrTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriOr,
|
||||
EvalFunc: evalOr,
|
||||
}
|
||||
}
|
||||
|
||||
func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
|
||||
func evalOr(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
||||
v, err = evalOrWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalOrWithoutShortcut(ctx, self)
|
||||
@@ -121,47 +127,47 @@ func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
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 leftBool, rightBool bool
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftBool, lok = ToBool(leftValue)
|
||||
rightBool, rok = ToBool(rightValue)
|
||||
leftBool, lok = kern.ToBool(leftValue)
|
||||
rightBool, rok = kern.ToBool(rightValue)
|
||||
|
||||
if lok && rok {
|
||||
v = leftBool || rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if err = self.checkOperands(); err != nil {
|
||||
if err = self.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||
if leftValue, err = self.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftBool, lok := ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue))
|
||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
|
||||
return
|
||||
} else if leftBool {
|
||||
v = true
|
||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||
if rightBool, rok := ToBool(rightValue); rok {
|
||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
||||
v = rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -169,7 +175,7 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymNot, newNotTerm)
|
||||
registerTermConstructor(SymAnd, newAndTerm)
|
||||
registerTermConstructor(SymOr, newOrTerm)
|
||||
scan.RegisterTermConstructor(scan.SymNot, newNotTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAnd, newAndTerm)
|
||||
scan.RegisterTermConstructor(scan.SymOr, newOrTerm)
|
||||
}
|
||||
|
||||
+25
-17
@@ -1,47 +1,55 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-builtin.go
|
||||
package expr
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"io"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- builtin term
|
||||
|
||||
func newBuiltinTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalBuiltin,
|
||||
func newBuiltinTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
count := 0
|
||||
if IsString(childValue) {
|
||||
if kern.IsString(childValue) {
|
||||
module, _ := childValue.(string)
|
||||
count, err = ImportInContextByGlobPattern(module)
|
||||
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||
} else {
|
||||
var moduleSpec any
|
||||
it := NewAnyIterator(childValue)
|
||||
var it kern.Iterator
|
||||
if it, err = NewIterator(ctx, childValue, nil); err != nil {
|
||||
return
|
||||
}
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if ImportInContext(module) {
|
||||
if ImportInContext(ctx, module) {
|
||||
count++
|
||||
} else {
|
||||
err = opTerm.Errorf("unknown builtin module %q", module)
|
||||
break
|
||||
}
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -57,5 +65,5 @@ func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-but.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- but term
|
||||
|
||||
func newButTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBut,
|
||||
evalFunc: evalBut,
|
||||
func newButTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBut,
|
||||
EvalFunc: evalBut,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBut(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
_, v, err = opTerm.evalInfix(ctx)
|
||||
func evalBut(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
_, v, err = opTerm.EvalInfix(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-context-value.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- context term
|
||||
|
||||
func newContextTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalContextValue,
|
||||
func newContextTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriIncDec,
|
||||
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 sourceCtx ExprContext
|
||||
if len(opTerm.children) == 0 {
|
||||
var sourceCtx kern.ExprContext
|
||||
if len(opTerm.Children) == 0 {
|
||||
sourceCtx = ctx
|
||||
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
||||
sourceCtx = globalCtx
|
||||
} else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
|
||||
} else if opTerm.Children[0].Symbol() == scan.SymVariable && opTerm.Children[0].Source() == "global" {
|
||||
sourceCtx = ctx.GetGlobal()
|
||||
} else if childValue, err = opTerm.EvalPrefix(ctx); err == nil {
|
||||
if dc, ok := childValue.(*dataCursor); ok {
|
||||
sourceCtx = dc.ctx
|
||||
}
|
||||
}
|
||||
|
||||
if sourceCtx != nil {
|
||||
if formatter, ok := sourceCtx.(DictFormat); ok {
|
||||
if formatter, ok := sourceCtx.(kern.DictFormat); ok {
|
||||
v = formatter.ToDict()
|
||||
} else if formatter, ok := sourceCtx.(Formatter); ok {
|
||||
} else if formatter, ok := sourceCtx.(kern.Formatter); ok {
|
||||
v = formatter.ToString(0)
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-ctrl.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- export all term
|
||||
|
||||
func newExportAllTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalExportAll,
|
||||
func newExportAllTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalExportAll,
|
||||
}
|
||||
}
|
||||
|
||||
func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
func evalExportAll(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
CtrlEnable(ctx, kern.ControlExportAll)
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-default.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- default term
|
||||
|
||||
func newDefaultTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalDefault,
|
||||
func newDefaultTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
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
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
|
||||
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
|
||||
}
|
||||
return
|
||||
@@ -40,32 +45,32 @@ func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- alternate term
|
||||
|
||||
func newAlternateTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalAlternate,
|
||||
func newAlternateTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
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
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
|
||||
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists && leftValue != nil {
|
||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
v = rightValue
|
||||
}
|
||||
} else {
|
||||
@@ -76,41 +81,41 @@ func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- default assign term
|
||||
|
||||
func newDefaultAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalAssignDefault,
|
||||
func newDefaultAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
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
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
|
||||
v = leftValue
|
||||
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
if functor, ok := rightValue.(Functor); ok {
|
||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
if functor, ok := rightValue.(kern.Functor); ok {
|
||||
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
||||
ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfDefault|kern.PfRepeat),
|
||||
})
|
||||
} else {
|
||||
v = rightValue
|
||||
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
||||
ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -118,7 +123,7 @@ func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
|
||||
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
|
||||
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleQuestion, newDefaultTerm)
|
||||
scan.RegisterTermConstructor(scan.SymQuestionEqual, newDefaultAssignTerm)
|
||||
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.
|
||||
|
||||
// operator-dot.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- dot term
|
||||
func newDotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDot,
|
||||
evalFunc: evalDot,
|
||||
func newDotTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDot,
|
||||
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
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
|
||||
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
indexTerm := opTerm.children[1]
|
||||
indexTerm := opTerm.Children[1]
|
||||
|
||||
switch unboxedValue := leftValue.(type) {
|
||||
case ExtIterator:
|
||||
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
|
||||
opName := indexTerm.source()
|
||||
case kern.ExtIterator:
|
||||
if indexTerm.Tk.Sym == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
||||
opName := indexTerm.Source()
|
||||
if unboxedValue.HasOperation(opName) {
|
||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||
} else {
|
||||
@@ -38,11 +43,27 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
v = false
|
||||
}
|
||||
} 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:
|
||||
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -50,5 +71,5 @@ func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-fact.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- fact term
|
||||
|
||||
func newFactTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPostfix,
|
||||
priority: priFact,
|
||||
evalFunc: evalFact,
|
||||
func newFactTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPostfix,
|
||||
Priority: scan.PriFact,
|
||||
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
|
||||
|
||||
if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(leftValue) {
|
||||
if kern.IsInteger(leftValue) {
|
||||
if i, _ := leftValue.(int64); i >= 0 {
|
||||
f := int64(1)
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(leftValue)
|
||||
err = opTerm.Errorf("incompatible type for factorial: %s", kern.TypeName(leftValue))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operand-fraction.go
|
||||
@@ -8,27 +8,29 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- fraction term
|
||||
func newFractionTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priFraction,
|
||||
evalFunc: evalFraction,
|
||||
func newFractionTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriFraction,
|
||||
EvalFunc: evalFraction,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- 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 num, den int64
|
||||
var ok bool
|
||||
|
||||
if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if numValue, denValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if num, ok = numValue.(int64); !ok {
|
||||
@@ -40,7 +42,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
if den == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
err = opTerm.Errorf("division by zero")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,16 +51,19 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
num = -num
|
||||
}
|
||||
if num != 0 {
|
||||
g := gcd(num, den)
|
||||
num = num / g
|
||||
den = den / g
|
||||
if g := kern.Gcd(num, den); g != 1 {
|
||||
num = num / g
|
||||
den = den / g
|
||||
}
|
||||
if den == 1 {
|
||||
v = num
|
||||
} else {
|
||||
v = &FractionType{num, den}
|
||||
// v = &expr.FractionType{num, den}
|
||||
v = kern.NewFraction(num, den)
|
||||
}
|
||||
} else {
|
||||
v = &FractionType{0, den}
|
||||
// v = &FractionType{0, den}
|
||||
v = kern.NewFraction(0, den)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -66,5 +71,5 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
// init
|
||||
func init() {
|
||||
// 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.
|
||||
|
||||
// operator-in.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- in term
|
||||
|
||||
func newInTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalIn,
|
||||
func newInTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
EvalFunc: evalIn,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +26,26 @@ func newInTerm(tk *Token) (inst *term) {
|
||||
// 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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsList(rightValue) {
|
||||
list, _ := rightValue.(*ListType)
|
||||
v = list.indexDeepSameCmp(leftValue) >= 0
|
||||
} else if IsDict(rightValue) {
|
||||
dict, _ := rightValue.(*DictType)
|
||||
v = dict.hasKey(leftValue)
|
||||
if kern.IsList(rightValue) {
|
||||
list, _ := rightValue.(*kern.ListType)
|
||||
v = list.IndexDeepSameCmp(leftValue) >= 0
|
||||
} else if kern.IsDict(rightValue) {
|
||||
dict, _ := rightValue.(*kern.DictType)
|
||||
v = dict.HasKey(leftValue)
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-include.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- include term
|
||||
|
||||
func newIncludeTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalInclude,
|
||||
func newIncludeTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
count := 0
|
||||
if IsList(childValue) {
|
||||
list, _ := childValue.(*ListType)
|
||||
if kern.IsList(childValue) {
|
||||
list, _ := childValue.(*kern.ListType)
|
||||
for i, filePathSpec := range *list {
|
||||
if filePath, ok := filePathSpec.(string); ok {
|
||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||
@@ -39,13 +44,13 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if IsString(childValue) {
|
||||
} else if kern.IsString(childValue) {
|
||||
filePath, _ := childValue.(string)
|
||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||
count++
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
if err != nil {
|
||||
//v = count
|
||||
@@ -56,5 +61,5 @@ func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-index.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- index term
|
||||
func newIndexTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDot,
|
||||
evalFunc: evalIndex,
|
||||
func newIndexTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDot,
|
||||
EvalFunc: evalIndex,
|
||||
}
|
||||
}
|
||||
|
||||
func verifyKey(indexList *ListType) (index any, err error) {
|
||||
func verifyKey(indexList *kern.ListType) (index any, err error) {
|
||||
index = (*indexList)[0]
|
||||
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
|
||||
|
||||
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 {
|
||||
v = maxValue + v
|
||||
}
|
||||
@@ -36,11 +41,11 @@ func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int,
|
||||
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)
|
||||
startIndex = v.a
|
||||
endIndex = v.b
|
||||
if endIndex == ConstLastIndex {
|
||||
if endIndex == kern.ConstLastIndex {
|
||||
endIndex = maxValue
|
||||
}
|
||||
if startIndex < 0 && startIndex >= -maxValue {
|
||||
@@ -59,17 +64,17 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
|
||||
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 indexList *ListType
|
||||
var indexList *kern.ListType
|
||||
var ok bool
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
indexTerm := opTerm.children[1]
|
||||
if indexList, ok = rightValue.(*ListType); !ok {
|
||||
indexTerm := opTerm.Children[1]
|
||||
if indexList, ok = rightValue.(*kern.ListType); !ok {
|
||||
err = opTerm.Errorf("invalid index expression")
|
||||
return
|
||||
} else if len(*indexList) != 1 {
|
||||
@@ -77,9 +82,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger((*indexList)[0]) {
|
||||
if kern.IsInteger((*indexList)[0]) {
|
||||
switch unboxedValue := leftValue.(type) {
|
||||
case *ListType:
|
||||
case *kern.ListType:
|
||||
var index int
|
||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||
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 {
|
||||
v = string(unboxedValue[index])
|
||||
}
|
||||
case *DictType:
|
||||
case *kern.DictType:
|
||||
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
||||
default:
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
} else if isIntPair((*indexList)[0]) {
|
||||
switch unboxedValue := leftValue.(type) {
|
||||
case *ListType:
|
||||
case *kern.ListType:
|
||||
var start, end int
|
||||
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||
sublist := ListType((*unboxedValue)[start:end])
|
||||
sublist := kern.ListType((*unboxedValue)[start:end])
|
||||
v = &sublist
|
||||
}
|
||||
case string:
|
||||
@@ -108,19 +113,19 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
v = unboxedValue[start:end]
|
||||
}
|
||||
default:
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
} else if IsDict(leftValue) {
|
||||
d := leftValue.(*DictType)
|
||||
} else if kern.IsDict(leftValue) {
|
||||
d := leftValue.(*kern.DictType)
|
||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||
} else {
|
||||
rightChild := opTerm.children[1]
|
||||
rightChild := opTerm.Children[1]
|
||||
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||
}
|
||||
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 indexValue any
|
||||
|
||||
@@ -134,5 +139,5 @@ func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue a
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-insert.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- prepend term
|
||||
|
||||
func newPrependTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priInsert,
|
||||
evalFunc: evalPrepend,
|
||||
func newPrependTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriInsert,
|
||||
EvalFunc: evalPrepend,
|
||||
}
|
||||
}
|
||||
|
||||
func newAppendTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priInsert,
|
||||
evalFunc: evalAppend,
|
||||
func newAppendTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriInsert,
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsList(rightValue) {
|
||||
list, _ := rightValue.(*ListType)
|
||||
newList := append(ListType{leftValue}, *list...)
|
||||
if kern.IsList(rightValue) {
|
||||
list, _ := rightValue.(*kern.ListType)
|
||||
newList := append(kern.ListType{leftValue}, *list...)
|
||||
v = &newList
|
||||
if opTerm.children[1].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||
if opTerm.Children[1].Symbol() == scan.SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.Children[1].Source(), v)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsList(leftValue) {
|
||||
list, _ := leftValue.(*ListType)
|
||||
if kern.IsList(leftValue) {
|
||||
list, _ := leftValue.(*kern.ListType)
|
||||
newList := append(*list, rightValue)
|
||||
v = &newList
|
||||
if opTerm.children[0].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||
if opTerm.Children[0].Symbol() == scan.SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.Children[0].Source(), v)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -86,6 +91,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPlusGreater, newPrependTerm)
|
||||
registerTermConstructor(SymLessPlus, newAppendTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPlusGreater, newPrependTerm)
|
||||
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.
|
||||
|
||||
// operator-iter-value.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- iter value term
|
||||
|
||||
func newIterValueTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priDereference,
|
||||
evalFunc: evalIterValue,
|
||||
func newIterValueTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriDereference,
|
||||
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
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if it, ok := childValue.(Iterator); ok {
|
||||
if it, ok := childValue.(kern.Iterator); ok {
|
||||
v, err = it.Current()
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -34,5 +39,5 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
// init
|
||||
func init() {
|
||||
// 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.
|
||||
|
||||
// operator-length.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- length term
|
||||
|
||||
func newLengthTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalLength,
|
||||
func newLengthTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsList(childValue) {
|
||||
ls, _ := childValue.(*ListType)
|
||||
if kern.IsList(childValue) {
|
||||
ls, _ := childValue.(*kern.ListType)
|
||||
v = int64(len(*ls))
|
||||
} else if IsString(childValue) {
|
||||
} else if kern.IsString(childValue) {
|
||||
s, _ := childValue.(string)
|
||||
v = int64(len(s))
|
||||
} else if IsDict(childValue) {
|
||||
m, _ := childValue.(*DictType)
|
||||
} else if kern.IsDict(childValue) {
|
||||
m, _ := childValue.(*kern.DictType)
|
||||
v = int64(len(*m))
|
||||
} else if it, ok := childValue.(Iterator); ok {
|
||||
} else if it, ok := childValue.(kern.Iterator); ok {
|
||||
v = int64(it.Count())
|
||||
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
||||
// count, _ := extIt.CallOperation(CountName, nil)
|
||||
@@ -41,12 +46,12 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
// v = int64(it.Index() + 1)
|
||||
// }
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymHash, newLengthTerm)
|
||||
scan.RegisterTermConstructor(scan.SymHash, newLengthTerm)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-map.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 newMapTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriIterOp,
|
||||
EvalFunc: evalMap,
|
||||
}
|
||||
}
|
||||
|
||||
func evalMap(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 MAP 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 {
|
||||
values.AppendItem(rightValue)
|
||||
}
|
||||
ctx.DeleteVar("_#")
|
||||
ctx.DeleteVar("__")
|
||||
ctx.DeleteVar("_")
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
v = values
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
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.
|
||||
|
||||
// operator-plugin.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- plugin term
|
||||
|
||||
func newPluginTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalPlugin,
|
||||
func newPluginTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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 count int
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if count, err = importPluginFromSearchPath(childValue); err == nil {
|
||||
if count, err = importPluginFromSearchPath(ctx, childValue); err == nil {
|
||||
v = int64(count)
|
||||
}
|
||||
return
|
||||
@@ -32,5 +37,5 @@ func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-post-inc-dec.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- post increment term
|
||||
|
||||
func newPostIncTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPostfix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPostInc,
|
||||
func newPostIncTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPostfix,
|
||||
Priority: scan.PriIncDec,
|
||||
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
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if it, ok := childValue.(Iterator); ok {
|
||||
if it, ok := childValue.(kern.Iterator); ok {
|
||||
var namePrefix string
|
||||
v, err = it.Next()
|
||||
|
||||
if opTerm.children[0].symbol() == SymVariable {
|
||||
namePrefix = opTerm.children[0].source()
|
||||
if opTerm.Children[0].Symbol() == scan.SymVariable {
|
||||
namePrefix = opTerm.Children[0].Source()
|
||||
}
|
||||
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
|
||||
if c, err1 := it.Current(); err1 == nil {
|
||||
ctx.UnsafeSetVar(namePrefix+"_current", c)
|
||||
}
|
||||
|
||||
if it.HasOperation(KeyName) {
|
||||
if k, err1 := it.CallOperation(KeyName, nil); err1 == nil {
|
||||
if it.HasOperation(kern.KeyName) {
|
||||
if k, err1 := it.CallOperation(kern.KeyName, nil); err1 == nil {
|
||||
ctx.UnsafeSetVar(namePrefix+"_key", k)
|
||||
}
|
||||
}
|
||||
if it.HasOperation(ValueName) {
|
||||
if v1, err1 := it.CallOperation(ValueName, nil); err1 == nil {
|
||||
if it.HasOperation(kern.ValueName) {
|
||||
if v1, err1 := it.CallOperation(kern.ValueName, nil); err1 == nil {
|
||||
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
|
||||
i, _ := childValue.(int64)
|
||||
ctx.SetVar(opTerm.children[0].source(), i+1)
|
||||
ctx.SetVar(opTerm.Children[0].Source(), i+1)
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -------- post decrement term
|
||||
|
||||
func newPostDecTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPostfix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPostDec,
|
||||
func newPostDecTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPostfix,
|
||||
Priority: scan.PriIncDec,
|
||||
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
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* if it, ok := childValue.(Iterator); ok {
|
||||
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
|
||||
i, _ := childValue.(int64)
|
||||
ctx.SetVar(opTerm.children[0].source(), i-1)
|
||||
ctx.SetVar(opTerm.Children[0].Source(), i-1)
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoublePlus, newPostIncTerm)
|
||||
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoublePlus, newPostIncTerm)
|
||||
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.
|
||||
|
||||
// operator-pre-inc-dec.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- pre increment term
|
||||
|
||||
func newPreIncTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreInc,
|
||||
func newPreIncTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriIncDec,
|
||||
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
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
||||
i := childValue.(int64) + 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
ctx.SetVar(opTerm.Children[0].Source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -------- pre decrement term
|
||||
|
||||
func newPreDecTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreDec,
|
||||
func newPreDecTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriIncDec,
|
||||
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
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
||||
i := childValue.(int64) - 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
ctx.SetVar(opTerm.Children[0].Source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPreInc, newPreIncTerm)
|
||||
registerTermConstructor(SymPreDec, newPreDecTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPreInc, newPreIncTerm)
|
||||
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.
|
||||
|
||||
// operator-prod.go
|
||||
@@ -6,45 +6,48 @@ package expr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- multiply term
|
||||
|
||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalMultiply,
|
||||
func newMultiplyTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriProduct,
|
||||
EvalFunc: evalMultiply,
|
||||
}
|
||||
}
|
||||
|
||||
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsString(leftValue) && IsInteger(rightValue) {
|
||||
func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
|
||||
s, _ := leftValue.(string)
|
||||
n, _ := rightValue.(int64)
|
||||
v = strings.Repeat(s, int(n))
|
||||
} else if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) * numAsFloat(rightValue)
|
||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||
v, err = mulAnyFract(leftValue, rightValue)
|
||||
} else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
||||
v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue)
|
||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
||||
v, err = kern.MulAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
rightInt, _ := rightValue.(int64)
|
||||
v = leftInt * rightInt
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = prodTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,44 +56,44 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
|
||||
//-------- divide term
|
||||
|
||||
func newDivideTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalDivide,
|
||||
func newDivideTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriProduct,
|
||||
EvalFunc: evalDivide,
|
||||
}
|
||||
}
|
||||
|
||||
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
||||
d := kern.NumAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
err = opTerm.ErrDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
v = kern.NumAsFloat(leftValue) / d
|
||||
}
|
||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||
v, err = divAnyFract(leftValue, rightValue)
|
||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
||||
v, err = kern.DivAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
err = opTerm.ErrDivisionByZero()
|
||||
} else {
|
||||
v = leftInt / rightInt
|
||||
}
|
||||
}
|
||||
} else if IsString(leftValue) && IsString(rightValue) {
|
||||
} else if kern.IsString(leftValue) && kern.IsString(rightValue) {
|
||||
source := leftValue.(string)
|
||||
sep := rightValue.(string)
|
||||
v = ListFromStrings(strings.Split(source, sep))
|
||||
} else if IsString(leftValue) && IsInteger(rightValue) {
|
||||
v = kern.ListFromStrings(strings.Split(source, sep))
|
||||
} else if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
|
||||
source := leftValue.(string)
|
||||
partSize := int(rightValue.(int64))
|
||||
if partSize == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
err = opTerm.ErrDivisionByZero()
|
||||
} else {
|
||||
partCount := 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 {
|
||||
parts = append(parts, source[len(source)-remainder:])
|
||||
}
|
||||
v = newList(parts)
|
||||
v = kern.NewList(parts)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,66 +128,66 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- divide as float term
|
||||
|
||||
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalDivideAsFloat,
|
||||
func newDivideAsFloatTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriProduct,
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = floatDivTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
||||
d := kern.NumAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = floatDivTerm.errDivisionByZero()
|
||||
err = floatDivTerm.ErrDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
v = kern.NumAsFloat(leftValue) / d
|
||||
}
|
||||
} else {
|
||||
err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- reminder term
|
||||
|
||||
func newRemainderTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalRemainder,
|
||||
func newRemainderTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriProduct,
|
||||
EvalFunc: evalRemainder,
|
||||
}
|
||||
}
|
||||
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
||||
rightInt, _ := rightValue.(int64)
|
||||
if rightInt == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
err = opTerm.ErrDivisionByZero()
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
v = leftInt % rightInt
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = remainderTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -193,8 +196,8 @@ func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||
registerTermConstructor(SymSlash, newDivideTerm)
|
||||
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
|
||||
registerTermConstructor(SymPercent, newRemainderTerm)
|
||||
scan.RegisterTermConstructor(scan.SymStar, newMultiplyTerm)
|
||||
scan.RegisterTermConstructor(scan.SymSlash, newDivideTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDotSlash, newDivideAsFloatTerm)
|
||||
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.
|
||||
|
||||
// operator-range.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- range term
|
||||
type intPair struct {
|
||||
@@ -12,10 +17,10 @@ type intPair struct {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -24,38 +29,38 @@ func isIntPair(v any) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func newRangeTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRange,
|
||||
evalFunc: evalRange,
|
||||
func newRangeTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRange,
|
||||
EvalFunc: evalRange,
|
||||
}
|
||||
}
|
||||
|
||||
func changeColonToRange(t *term) {
|
||||
if t.tk.IsSymbol(SymColon) {
|
||||
t.tk.Sym = SymRange
|
||||
t.evalFunc = evalRange
|
||||
func changeColonToRange(t *scan.Term) {
|
||||
if t.Tk.IsSymbol(scan.SymColon) {
|
||||
t.Tk.Sym = scan.SymRange
|
||||
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
|
||||
|
||||
if len(opTerm.children) == 0 {
|
||||
if len(opTerm.Children) == 0 {
|
||||
leftValue = int64(0)
|
||||
rightValue = int64(-1)
|
||||
} else if len(opTerm.children) == 1 {
|
||||
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
|
||||
} else if len(opTerm.Children) == 1 {
|
||||
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
rightValue = int64(ConstLastIndex)
|
||||
} else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
rightValue = int64(kern.ConstLastIndex)
|
||||
} else if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||
if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
|
||||
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = errRangeInvalidSpecification(opTerm)
|
||||
return
|
||||
@@ -68,15 +73,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func errRangeInvalidSpecification(t *term) error {
|
||||
func errRangeInvalidSpecification(t *scan.Term) error {
|
||||
return t.Errorf("invalid range specification")
|
||||
}
|
||||
|
||||
func errRangeUnexpectedExpression(t *term) error {
|
||||
func errRangeUnexpectedExpression(t *scan.Term) error {
|
||||
return t.Errorf("unexpected range expression")
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-rel.go
|
||||
package expr
|
||||
|
||||
import "reflect"
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- equal term
|
||||
|
||||
func newEqualTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalEqual,
|
||||
func newEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
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) {
|
||||
if isNumOrFract(a) && isNumOrFract(b) {
|
||||
if IsNumber(a) && IsNumber(b) {
|
||||
if IsInteger(a) && IsInteger(b) {
|
||||
func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
|
||||
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
|
||||
if kern.IsNumber(a) && kern.IsNumber(b) {
|
||||
if kern.IsInteger(a) && kern.IsInteger(b) {
|
||||
li, _ := a.(int64)
|
||||
ri, _ := b.(int64)
|
||||
eq = li == ri
|
||||
} else {
|
||||
eq = numAsFloat(a) == numAsFloat(b)
|
||||
eq = kern.NumAsFloat(a) == kern.NumAsFloat(b)
|
||||
}
|
||||
} else {
|
||||
var cmp int
|
||||
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||
if cmp, err = kern.CmpAnyFract(a, b); err == nil {
|
||||
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)
|
||||
} else {
|
||||
eq = reflect.DeepEqual(a, b)
|
||||
@@ -45,10 +50,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -58,19 +63,19 @@ func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- not equal term
|
||||
|
||||
func newNotEqualTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalNotEqual,
|
||||
func newNotEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
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 {
|
||||
b, _ := ToBool(v)
|
||||
b, _ := kern.ToBool(v)
|
||||
v = !b
|
||||
}
|
||||
return
|
||||
@@ -78,51 +83,51 @@ func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- less term
|
||||
|
||||
func newLessTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalLess,
|
||||
func newLessTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
EvalFunc: evalLess,
|
||||
}
|
||||
}
|
||||
|
||||
func lessThan(self *term, a, b any) (isLess bool, err error) {
|
||||
if isNumOrFract(a) && isNumOrFract(b) {
|
||||
if IsNumber(a) && IsNumber(b) {
|
||||
if IsInteger(a) && IsInteger(b) {
|
||||
func lessThan(self *scan.Term, a, b any) (isLess bool, err error) {
|
||||
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
|
||||
if kern.IsNumber(a) && kern.IsNumber(b) {
|
||||
if kern.IsInteger(a) && kern.IsInteger(b) {
|
||||
li, _ := a.(int64)
|
||||
ri, _ := b.(int64)
|
||||
isLess = li < ri
|
||||
} else {
|
||||
isLess = numAsFloat(a) < numAsFloat(b)
|
||||
isLess = kern.NumAsFloat(a) < kern.NumAsFloat(b)
|
||||
}
|
||||
} else {
|
||||
var cmp int
|
||||
if cmp, err = cmpAnyFract(a, b); err == nil {
|
||||
if cmp, err = kern.CmpAnyFract(a, b); err == nil {
|
||||
isLess = cmp < 0
|
||||
}
|
||||
}
|
||||
} else if IsString(a) && IsString(b) {
|
||||
} else if kern.IsString(a) && kern.IsString(b) {
|
||||
ls, _ := a.(string)
|
||||
rs, _ := b.(string)
|
||||
isLess = ls < rs
|
||||
// Inclusion test
|
||||
} else if IsList(a) && IsList(b) {
|
||||
aList, _ := a.(*ListType)
|
||||
bList, _ := b.(*ListType)
|
||||
isLess = len(*aList) < len(*bList) && bList.contains(aList)
|
||||
} else if kern.IsList(a) && kern.IsList(b) {
|
||||
aList, _ := a.(*kern.ListType)
|
||||
bList, _ := b.(*kern.ListType)
|
||||
isLess = len(*aList) < len(*bList) && bList.Contains(aList)
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(a, b)
|
||||
err = self.ErrIncompatibleTypes(a, b)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
v, err = lessThan(opTerm, leftValue, rightValue)
|
||||
@@ -131,21 +136,21 @@ func evalLess(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- less or equal term
|
||||
|
||||
func newLessEqualTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalLessEqual,
|
||||
func newLessEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
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 {
|
||||
if IsList(a) && IsList(b) {
|
||||
isLessEq, err = sameContent(a, b)
|
||||
if kern.IsList(a) && kern.IsList(b) {
|
||||
isLessEq, err = kern.SameContent(a, b)
|
||||
} else {
|
||||
isLessEq, err = equals(a, b, nil)
|
||||
}
|
||||
@@ -154,10 +159,10 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -168,20 +173,20 @@ func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- greater term
|
||||
|
||||
func newGreaterTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalGreater,
|
||||
func newGreaterTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -191,20 +196,20 @@ func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- greater or equal term
|
||||
|
||||
func newGreaterEqualTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalGreaterEqual,
|
||||
func newGreaterEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriRelational,
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -214,10 +219,10 @@ func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleEqual, newEqualTerm)
|
||||
registerTermConstructor(SymNotEqual, newNotEqualTerm)
|
||||
registerTermConstructor(SymLess, newLessTerm)
|
||||
registerTermConstructor(SymLessOrEqual, newLessEqualTerm)
|
||||
registerTermConstructor(SymGreater, newGreaterTerm)
|
||||
registerTermConstructor(SymGreaterOrEqual, newGreaterEqualTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleEqual, newEqualTerm)
|
||||
scan.RegisterTermConstructor(scan.SymNotEqual, newNotEqualTerm)
|
||||
scan.RegisterTermConstructor(scan.SymLess, newLessTerm)
|
||||
scan.RegisterTermConstructor(scan.SymLessOrEqual, newLessEqualTerm)
|
||||
scan.RegisterTermConstructor(scan.SymGreater, newGreaterTerm)
|
||||
scan.RegisterTermConstructor(scan.SymGreaterOrEqual, newGreaterEqualTerm)
|
||||
}
|
||||
|
||||
+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.
|
||||
|
||||
// operator-selector.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- selector term
|
||||
|
||||
func newSelectorTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 3),
|
||||
position: posInfix,
|
||||
priority: priSelector,
|
||||
evalFunc: evalSelector,
|
||||
func newSelectorTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 3),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriSelector,
|
||||
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)
|
||||
if caseData.filterList == nil {
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
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 {
|
||||
var valueAsInt = int64(0)
|
||||
if b, ok := exprValue.(bool); ok {
|
||||
@@ -38,7 +43,7 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
||||
} else {
|
||||
var caseValue any
|
||||
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)
|
||||
match = true
|
||||
break
|
||||
@@ -49,33 +54,33 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
||||
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 match bool
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
exprTerm := opTerm.children[0]
|
||||
if exprValue, err = exprTerm.compute(ctx); err != nil {
|
||||
exprTerm := opTerm.Children[0]
|
||||
if exprValue, err = exprTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
caseListTerm := opTerm.children[1]
|
||||
caseList, _ := caseListTerm.value().([]*term)
|
||||
caseListTerm := opTerm.Children[1]
|
||||
caseList, _ := caseListTerm.Value().([]*scan.Term)
|
||||
for i, caseTerm := range caseList {
|
||||
caseSel := caseTerm.value()
|
||||
caseSel := caseTerm.Value()
|
||||
if match, v, err = trySelectorCase(ctx, exprValue, caseSel, i); err != nil || match {
|
||||
break
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
// operator-shift.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- bit right shift term
|
||||
|
||||
func newRightShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalRightShift,
|
||||
func newRightShiftTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBinShift,
|
||||
EvalFunc: evalRightShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt >> rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -38,31 +43,31 @@ func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func newLeftShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalLeftShift,
|
||||
func newLeftShiftTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBinShift,
|
||||
EvalFunc: evalLeftShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt << rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -72,6 +77,6 @@ func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
|
||||
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleGreater, newRightShiftTerm)
|
||||
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.
|
||||
|
||||
// operator-sign.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- plus sign term
|
||||
|
||||
func newPlusSignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
// class: classOperator,
|
||||
// kind: kindUnknown,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalSign,
|
||||
func newPlusSignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
EvalFunc: evalSign,
|
||||
}
|
||||
}
|
||||
|
||||
func newMinusSignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalSign,
|
||||
func newMinusSignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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
|
||||
|
||||
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if rightValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsFloat(rightValue) {
|
||||
if opTerm.tk.Sym == SymChangeSign {
|
||||
if kern.IsFloat(rightValue) {
|
||||
if opTerm.Tk.Sym == scan.SymChangeSign {
|
||||
f, _ := rightValue.(float64)
|
||||
v = -f
|
||||
} else {
|
||||
v = rightValue
|
||||
}
|
||||
} else if IsInteger(rightValue) {
|
||||
if opTerm.tk.Sym == SymChangeSign {
|
||||
} else if kern.IsInteger(rightValue) {
|
||||
if opTerm.Tk.Sym == scan.SymChangeSign {
|
||||
i, _ := rightValue.(int64)
|
||||
v = -i
|
||||
} else {
|
||||
v = rightValue
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(rightValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymUnchangeSign, newPlusSignTerm)
|
||||
registerTermConstructor(SymChangeSign, newMinusSignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymUnchangeSign, newPlusSignTerm)
|
||||
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.
|
||||
|
||||
// operator-sum.go
|
||||
@@ -7,64 +7,67 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- plus term
|
||||
|
||||
func newPlusTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priSum,
|
||||
evalFunc: evalPlus,
|
||||
func newPlusTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriSum,
|
||||
EvalFunc: evalPlus,
|
||||
}
|
||||
}
|
||||
|
||||
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||
func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if (kern.IsString(leftValue) && kern.IsNumberString(rightValue)) || (kern.IsString(rightValue) && kern.IsNumberString(leftValue)) {
|
||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) + numAsFloat(rightValue)
|
||||
} else if kern.IsNumber(leftValue) && kern.IsNumber(rightValue) {
|
||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
||||
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
rightInt, _ := rightValue.(int64)
|
||||
v = leftInt + rightInt
|
||||
}
|
||||
} else if IsList(leftValue) && IsList(rightValue) {
|
||||
var leftList, rightList *ListType
|
||||
leftList, _ = leftValue.(*ListType)
|
||||
rightList, _ = rightValue.(*ListType)
|
||||
} else if kern.IsList(leftValue) && kern.IsList(rightValue) {
|
||||
var leftList, rightList *kern.ListType
|
||||
leftList, _ = leftValue.(*kern.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, *rightList...)
|
||||
v = &sumList
|
||||
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) + numAsFloat(rightValue)
|
||||
} else if (kern.IsFraction(leftValue) && kern.IsNumber(rightValue)) || (kern.IsFraction(rightValue) && kern.IsNumber(leftValue)) {
|
||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
||||
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
|
||||
} else {
|
||||
v, err = sumAnyFract(leftValue, rightValue)
|
||||
v, err = kern.SumAnyFract(leftValue, rightValue)
|
||||
}
|
||||
} else if IsDict(leftValue) && IsDict(rightValue) {
|
||||
leftDict, _ := leftValue.(*DictType)
|
||||
rightDict, _ := rightValue.(*DictType)
|
||||
c := leftDict.clone()
|
||||
c.merge(rightDict)
|
||||
} else if kern.IsDict(leftValue) && kern.IsDict(rightValue) {
|
||||
leftDict, _ := leftValue.(*kern.DictType)
|
||||
rightDict, _ := rightValue.(*kern.DictType)
|
||||
c := leftDict.Clone()
|
||||
c.Merge(rightDict)
|
||||
v = c
|
||||
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||
v, err = sumAnyFract(leftValue, rightValue)
|
||||
} else if kern.IsFraction(leftValue) && kern.IsFraction(rightValue) {
|
||||
v, err = kern.SumAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = plusTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -73,31 +76,31 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
|
||||
//-------- minus term
|
||||
|
||||
func newMinusTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priSum,
|
||||
evalFunc: evalMinus,
|
||||
func newMinusTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriSum,
|
||||
EvalFunc: evalMinus,
|
||||
}
|
||||
}
|
||||
|
||||
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||
v, err = subAnyFract(leftValue, rightValue)
|
||||
func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
||||
v = kern.NumAsFloat(leftValue) - kern.NumAsFloat(rightValue)
|
||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
||||
v, err = kern.SubAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
rightInt, _ := rightValue.(int64)
|
||||
v = leftInt - rightInt
|
||||
}
|
||||
} else if IsList(leftValue) && IsList(rightValue) {
|
||||
leftList, _ := leftValue.(*ListType)
|
||||
rightList, _ := rightValue.(*ListType)
|
||||
diffList := make(ListType, 0, len(*leftList)-len(*rightList))
|
||||
} else if kern.IsList(leftValue) && kern.IsList(rightValue) {
|
||||
leftList, _ := leftValue.(*kern.ListType)
|
||||
rightList, _ := rightValue.(*kern.ListType)
|
||||
diffList := make(kern.ListType, 0, len(*leftList)-len(*rightList))
|
||||
for _, item := range *leftList {
|
||||
if slices.Index(*rightList, item) < 0 {
|
||||
diffList = append(diffList, item)
|
||||
@@ -105,15 +108,15 @@ func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
}
|
||||
v = &diffList
|
||||
} else {
|
||||
err = minusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = minusTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
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
|
||||
|
||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = minusTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -122,6 +125,6 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPlus, newPlusTerm)
|
||||
registerTermConstructor(SymMinus, newMinusTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPlus, newPlusTerm)
|
||||
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.
|
||||
|
||||
// operator-unset.go
|
||||
package expr
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- unset term
|
||||
|
||||
func newUnsetTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priSign,
|
||||
evalFunc: evalUnset,
|
||||
func newUnsetTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriSign,
|
||||
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 {
|
||||
var size int
|
||||
if strings.HasSuffix(name, "()") {
|
||||
@@ -31,22 +36,22 @@ func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, e
|
||||
deleted = ctx.VarCount() < size
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(item)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(item)
|
||||
}
|
||||
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 deleted bool
|
||||
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if childValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
count := 0
|
||||
if IsList(childValue) {
|
||||
list, _ := childValue.(*ListType)
|
||||
if kern.IsList(childValue) {
|
||||
list, _ := childValue.(*kern.ListType)
|
||||
for _, item := range *list {
|
||||
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
|
||||
break
|
||||
@@ -65,5 +70,5 @@ func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// 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.
|
||||
|
||||
// parser.go
|
||||
@@ -7,6 +7,7 @@ package expr
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
@@ -54,34 +55,34 @@ func NewParser() (p *parser) {
|
||||
return p
|
||||
}
|
||||
|
||||
func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
||||
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() {
|
||||
func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
|
||||
for tk = scanner.Next(); tk.IsSymbol(scan.SymComment); tk = scanner.Next() {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||
args := make([]*term, 0, 10)
|
||||
func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) {
|
||||
args := make([]*scan.Term, 0, 10)
|
||||
itemExpected := false
|
||||
lastSym := SymUnknown
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||
lastSym := scan.SymUnknown
|
||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
||||
var subTree *scan.Ast
|
||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err != nil {
|
||||
break
|
||||
}
|
||||
prev := scanner.Previous()
|
||||
if subTree.root != nil {
|
||||
args = append(args, subTree.root)
|
||||
if subTree.Root() != nil {
|
||||
args = append(args, subTree.Root())
|
||||
} else if itemExpected {
|
||||
err = prev.ErrorExpectedGot("function-param-value")
|
||||
break
|
||||
}
|
||||
|
||||
itemExpected = prev.Sym == SymComma
|
||||
itemExpected = prev.Sym == scan.SymComma
|
||||
lastSym = scanner.Previous().Sym
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedRound {
|
||||
if lastSym != scan.SymClosedRound {
|
||||
err = errors.New("unterminated arguments list")
|
||||
} else {
|
||||
tree = newFuncCallTerm(tk, args)
|
||||
@@ -90,33 +91,33 @@ func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Tok
|
||||
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}
|
||||
var body *ast
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
var body *scan.Ast
|
||||
args := make([]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
defaultParamsStarted := false
|
||||
itemExpected := false
|
||||
tk := scanner.Previous()
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymIdentifier) {
|
||||
param := newTerm(tk)
|
||||
if tk.IsSymbol(scan.SymIdentifier) {
|
||||
param := scan.NewTerm(tk)
|
||||
if len(args) > 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
|
||||
}
|
||||
}
|
||||
args = append(args, param)
|
||||
tk = parser.Next(scanner)
|
||||
if tk.Sym == SymEqual {
|
||||
var paramExpr *ast
|
||||
if tk.Sym == scan.SymEqual {
|
||||
var paramExpr *scan.Ast
|
||||
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
|
||||
}
|
||||
param.forceChild(paramExpr.root)
|
||||
param.ForceChild(paramExpr.Root())
|
||||
} else if defaultParamsStarted {
|
||||
err = tk.Errorf("can't mix default and non-default parameters")
|
||||
break
|
||||
@@ -127,74 +128,74 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
break
|
||||
}
|
||||
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(")")
|
||||
}
|
||||
if err == nil {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymOpenBrace) {
|
||||
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||
if tk.IsSymbol(scan.SymOpenBrace) {
|
||||
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymClosedBrace)
|
||||
} else {
|
||||
err = tk.ErrorExpectedGot("{")
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if scanner.Previous().Sym != SymClosedBrace {
|
||||
if scanner.Previous().Sym != scan.SymClosedBrace {
|
||||
err = scanner.Previous().ErrorExpectedGot("}")
|
||||
} else {
|
||||
tk = scanner.makeValueToken(SymExpression, "", body)
|
||||
tk = scanner.MakeValueToken(scan.SymExpression, "", body)
|
||||
tree = newFuncDefTerm(tk, args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func paramAlreadyDefined(args []*term, param *term) (position int) {
|
||||
func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
|
||||
position = 0
|
||||
for i, arg := range args {
|
||||
if arg.source() == param.source() {
|
||||
if arg.Source() == param.Source() {
|
||||
position = i + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||
r, c := scanner.lastPos()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
|
||||
r, c := scanner.LastPos()
|
||||
args := make([]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
itemCtx := remFlags(ctx, allowIndex)
|
||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||
zeroRequired := scanner.current.Sym == SymColon
|
||||
var itemTree *ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||
root := itemTree.root
|
||||
for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
|
||||
zeroRequired := scanner.Current().Sym == scan.SymColon
|
||||
var itemTree *scan.Ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
|
||||
root := itemTree.Root()
|
||||
if root != nil {
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
||||
changeColonToRange(root)
|
||||
}
|
||||
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||
if !hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange {
|
||||
// err = root.Errorf("unexpected range expression")
|
||||
err = errRangeUnexpectedExpression(root)
|
||||
break
|
||||
}
|
||||
args = append(args, root)
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||
if len(root.children) == 1 {
|
||||
root.children = append(root.children, root.children[0])
|
||||
} else if len(root.children) > 1 {
|
||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange && zeroRequired { //len(root.children) == 0 {
|
||||
if len(root.Children) == 1 {
|
||||
root.Children = append(root.Children, root.Children[0])
|
||||
} else if len(root.Children) > 1 {
|
||||
// err = root.Errorf("invalid range specification")
|
||||
err = errRangeInvalidSpecification(root)
|
||||
break
|
||||
}
|
||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||
zeroTerm := newTerm(zeroTk)
|
||||
zeroTerm.setParent(root)
|
||||
root.children[0] = zeroTerm
|
||||
zeroTk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymInteger, "0", int64(0))
|
||||
zeroTerm := scan.NewTerm(zeroTk)
|
||||
zeroTerm.SetParent(root)
|
||||
root.Children[0] = zeroTerm
|
||||
}
|
||||
} else if itemExpected {
|
||||
prev := scanner.Previous()
|
||||
@@ -205,12 +206,12 @@ func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *
|
||||
break
|
||||
}
|
||||
lastSym = scanner.Previous().Sym
|
||||
if itemExpected = lastSym == SymComma; itemExpected {
|
||||
if itemExpected = lastSym == scan.SymComma; itemExpected {
|
||||
remFlags(ctx, allowIndex)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedSquare {
|
||||
if lastSym != scan.SymClosedSquare {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
listTerm = newListTerm(r, c, args)
|
||||
@@ -219,16 +220,16 @@ func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *
|
||||
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()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
args := make([]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||
if subTree.root != nil {
|
||||
args = append(args, subTree.root)
|
||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
||||
var subTree *scan.Ast
|
||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err == nil {
|
||||
if subTree.Root() != nil {
|
||||
args = append(args, subTree.Root())
|
||||
} else if itemExpected {
|
||||
prev := scanner.Previous()
|
||||
err = prev.ErrorExpectedGot("iterator-param")
|
||||
@@ -238,10 +239,10 @@ func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree
|
||||
break
|
||||
}
|
||||
lastSym = scanner.Previous().Sym
|
||||
itemExpected = lastSym == SymComma
|
||||
itemExpected = lastSym == scan.SymComma
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedRound {
|
||||
if lastSym != scan.SymClosedRound {
|
||||
err = scanner.Previous().ErrorExpectedGot(")")
|
||||
} else {
|
||||
subtree = newIteratorTerm(tk, args)
|
||||
@@ -250,18 +251,18 @@ func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree
|
||||
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)
|
||||
if tk.Sym == SymError {
|
||||
if tk.Sym == scan.SymError {
|
||||
err = tk.Error()
|
||||
return
|
||||
}
|
||||
if tk.Sym == SymClosedBrace || tk.Sym == SymEos {
|
||||
if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||
return
|
||||
}
|
||||
if tk.Sym == SymInteger || tk.Sym == SymString {
|
||||
if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
|
||||
tkSep := parser.Next(scanner)
|
||||
if tkSep.Sym != SymColon {
|
||||
if tkSep.Sym != scan.SymColon {
|
||||
err = tkSep.ErrorExpectedGot(":")
|
||||
} else {
|
||||
key = tk.Value
|
||||
@@ -272,12 +273,12 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||
args := make(map[any]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
|
||||
args := make(map[any]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != SymClosedBrace && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
||||
var subTree *scan.Ast
|
||||
var key any
|
||||
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||
break
|
||||
@@ -289,9 +290,9 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
|
||||
}
|
||||
break
|
||||
}
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||
if subTree.root != nil {
|
||||
args[key] = subTree.root
|
||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||
if subTree.Root() != nil {
|
||||
args[key] = subTree.Root()
|
||||
} else /*if key != nil*/ {
|
||||
prev := scanner.Previous()
|
||||
err = prev.ErrorExpectedGot("dictionary-value")
|
||||
@@ -301,10 +302,10 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
|
||||
break
|
||||
}
|
||||
lastSym = scanner.Previous().Sym
|
||||
itemExpected = lastSym == SymComma
|
||||
itemExpected = lastSym == scan.SymComma
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedBrace {
|
||||
if lastSym != scan.SymClosedBrace {
|
||||
err = scanner.Previous().ErrorExpectedGot("}")
|
||||
} else {
|
||||
subtree = newDictTerm(args)
|
||||
@@ -313,14 +314,14 @@ func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subt
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||
var filterList *term
|
||||
var caseExpr *ast
|
||||
func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext, defaultCase bool) (caseTerm *scan.Term, err error) {
|
||||
var filterList *scan.Term
|
||||
var caseExpr *scan.Ast
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := parser.Next(scanner)
|
||||
startRow := tk.row
|
||||
startCol := tk.col
|
||||
if tk.Sym == SymOpenSquare {
|
||||
startRow := tk.Row()
|
||||
startCol := tk.Col()
|
||||
if tk.Sym == scan.SymOpenSquare {
|
||||
if defaultCase {
|
||||
err = tk.Errorf("case list in default clause")
|
||||
return
|
||||
@@ -329,14 +330,14 @@ func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, def
|
||||
return
|
||||
}
|
||||
tk = parser.Next(scanner)
|
||||
startRow = tk.row
|
||||
startCol = tk.col
|
||||
startRow = tk.Row()
|
||||
startCol = tk.Col()
|
||||
} else if !defaultCase {
|
||||
filterList = newListTerm(startRow, startCol, make([]*term, 0))
|
||||
filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0))
|
||||
}
|
||||
|
||||
if tk.Sym == SymOpenBrace {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||
if tk.Sym == scan.SymOpenBrace {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, scan.SymClosedBrace); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -349,25 +350,25 @@ func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, def
|
||||
return
|
||||
}
|
||||
|
||||
func addSelectorCase(selectorTerm, caseTerm *term) {
|
||||
if len(selectorTerm.children) < 2 {
|
||||
func addSelectorCase(selectorTerm, caseTerm *scan.Term) {
|
||||
if len(selectorTerm.Children) < 2 {
|
||||
caseListTerm := newListTermA(caseTerm)
|
||||
selectorTerm.children = append(selectorTerm.children, caseListTerm)
|
||||
selectorTerm.Children = append(selectorTerm.Children, caseListTerm)
|
||||
} else {
|
||||
caseListTerm := selectorTerm.children[1]
|
||||
caseList, _ := caseListTerm.value().([]*term)
|
||||
caseListTerm := selectorTerm.Children[1]
|
||||
caseList, _ := caseListTerm.Value().([]*scan.Term)
|
||||
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) {
|
||||
var caseTerm *term
|
||||
func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx parserContext) (selectorTerm *scan.Term, err error) {
|
||||
var caseTerm *scan.Term
|
||||
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := scanner.makeToken(SymSelector, '?')
|
||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||
tk := scanner.MakeToken(scan.SymSelector, '?')
|
||||
if selectorTerm, err = tree.AddToken(tk); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -377,61 +378,70 @@ func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserConte
|
||||
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...)
|
||||
}
|
||||
|
||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
termSymbols = append(termSymbols, SymEos)
|
||||
func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
|
||||
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...)
|
||||
}
|
||||
|
||||
func couldBeACollection(t *term) bool {
|
||||
var sym = SymUnknown
|
||||
func couldBeACollection(t *scan.Term) bool {
|
||||
var sym = scan.SymUnknown
|
||||
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) {
|
||||
var tk *Token
|
||||
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
||||
var tk *scan.Token
|
||||
if allowIndeces {
|
||||
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
root = newTerm(tk)
|
||||
if err = tree.addTerm(root); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), scan.SymIndex, listTerm.Source())
|
||||
root = scan.NewTerm(tk)
|
||||
if err = tree.AddTerm(root); err == nil {
|
||||
err = tree.AddTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
root = listTerm
|
||||
err = tree.addTerm(listTerm)
|
||||
err = tree.AddTerm(listTerm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func changePrefix(tk *Token) {
|
||||
func changePrefix(tk *scan.Token) {
|
||||
switch tk.Sym {
|
||||
case SymMinus:
|
||||
tk.SetSymbol(SymChangeSign)
|
||||
case SymPlus:
|
||||
tk.SetSymbol(SymUnchangeSign)
|
||||
case SymStar:
|
||||
tk.SetSymbol(SymDereference)
|
||||
case SymExclamation:
|
||||
tk.SetSymbol(SymNot)
|
||||
case SymDoublePlus:
|
||||
tk.SetSymbol(SymPreInc)
|
||||
case SymDoubleMinus:
|
||||
tk.SetSymbol(SymPreDec)
|
||||
case scan.SymMinus:
|
||||
tk.SetSymbol(scan.SymChangeSign)
|
||||
case scan.SymPlus:
|
||||
tk.SetSymbol(scan.SymUnchangeSign)
|
||||
case scan.SymStar:
|
||||
tk.SetSymbol(scan.SymDereference)
|
||||
case scan.SymExclamation:
|
||||
tk.SetSymbol(scan.SymNot)
|
||||
case scan.SymDoublePlus:
|
||||
tk.SetSymbol(scan.SymPreInc)
|
||||
case scan.SymDoubleMinus:
|
||||
tk.SetSymbol(scan.SymPreDec)
|
||||
}
|
||||
}
|
||||
|
||||
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
var selectorTerm *term = nil
|
||||
var currentTerm *term = nil
|
||||
var tk *Token
|
||||
func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
|
||||
var selectorTerm *scan.Term = nil
|
||||
var currentTerm *scan.Term = nil
|
||||
var tk *scan.Token
|
||||
|
||||
tree = NewAst()
|
||||
tree = scan.NewAst()
|
||||
firstToken := true
|
||||
// lastSym := SymUnknown
|
||||
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
|
||||
// }
|
||||
|
||||
if tk.Sym == SymSemiColon {
|
||||
if tk.Sym == scan.SymSemiColon {
|
||||
if hasFlag(ctx, allowMultiExpr) {
|
||||
tree.ToForest()
|
||||
firstToken = true
|
||||
@@ -447,7 +457,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
selectorTerm = nil
|
||||
continue
|
||||
} else {
|
||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source)
|
||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -455,99 +465,91 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
//fmt.Println("Token:", tk)
|
||||
if firstToken {
|
||||
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
|
||||
}
|
||||
|
||||
switch tk.Sym {
|
||||
case SymOpenRound:
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||
exprTerm := newExprTerm(subTree.root)
|
||||
err = tree.addTerm(exprTerm)
|
||||
currentTerm = exprTerm
|
||||
// subTree.root.priority = priValue
|
||||
// err = tree.addTerm(newExprTerm(subTree.root))
|
||||
// currentTerm = subTree.root
|
||||
case scan.SymOpenRound:
|
||||
var subTree *scan.Ast
|
||||
if subTree, err = parser.parseGeneral(scanner, ctx, scan.SymClosedRound); err == nil {
|
||||
if subTree.Root() == nil {
|
||||
err = tk.ErrorExpectedGotString("expression", "()")
|
||||
} else {
|
||||
exprTerm := newExprTerm(subTree.Root())
|
||||
err = tree.AddTerm(exprTerm)
|
||||
currentTerm = exprTerm
|
||||
}
|
||||
}
|
||||
case SymFuncCall:
|
||||
var funcCallTerm *term
|
||||
case scan.SymFuncCall:
|
||||
var funcCallTerm *scan.Term
|
||||
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||
err = tree.addTerm(funcCallTerm)
|
||||
err = tree.AddTerm(funcCallTerm)
|
||||
currentTerm = funcCallTerm
|
||||
}
|
||||
case SymOpenSquare:
|
||||
var listTerm *term
|
||||
case scan.SymOpenSquare:
|
||||
var listTerm *scan.Term
|
||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case SymOpenBrace:
|
||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||
case scan.SymOpenBrace:
|
||||
if currentTerm != nil && currentTerm.Symbol() == scan.SymColon {
|
||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||
} else {
|
||||
var mapTerm *term
|
||||
var mapTerm *scan.Term
|
||||
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(mapTerm)
|
||||
err = tree.AddTerm(mapTerm)
|
||||
currentTerm = mapTerm
|
||||
}
|
||||
}
|
||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
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)
|
||||
firstToken = true
|
||||
case SymFuncDef:
|
||||
var funcDefTerm *term
|
||||
case scan.SymFuncDef:
|
||||
var funcDefTerm *scan.Term
|
||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||
err = tree.addTerm(funcDefTerm)
|
||||
err = tree.AddTerm(funcDefTerm)
|
||||
currentTerm = funcDefTerm
|
||||
}
|
||||
case SymDollarRound:
|
||||
var iterDefTerm *term
|
||||
case scan.SymDollarRound:
|
||||
var iterDefTerm *scan.Term
|
||||
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(iterDefTerm)
|
||||
err = tree.AddTerm(iterDefTerm)
|
||||
currentTerm = iterDefTerm
|
||||
}
|
||||
case SymIdentifier:
|
||||
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||
case scan.SymIdentifier:
|
||||
if tk.Source()[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.Source())
|
||||
} else {
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
currentTerm, err = tree.AddToken(tk)
|
||||
}
|
||||
case SymQuestion:
|
||||
case scan.SymQuestion:
|
||||
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||
currentTerm = selectorTerm
|
||||
addFlags(ctx, selectorContext)
|
||||
}
|
||||
case SymColon, SymDoubleColon:
|
||||
var caseTerm *term
|
||||
case scan.SymColon, scan.SymDoubleColon:
|
||||
var caseTerm *scan.Term
|
||||
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)
|
||||
currentTerm = caseTerm
|
||||
if tk.Sym == SymDoubleColon {
|
||||
if tk.Sym == scan.SymDoubleColon {
|
||||
selectorTerm = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
if tk.IsOneOfA(SymColon, SymRange) {
|
||||
currentTerm, err = tree.AddToken(tk)
|
||||
if tk.IsOneOfA(scan.SymColon, scan.SymRange) {
|
||||
// Colon outside a selector term acts like a separator
|
||||
firstToken = true
|
||||
}
|
||||
}
|
||||
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
|
||||
remFlags(ctx, selectorContext)
|
||||
}
|
||||
@@ -557,12 +559,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
if err == nil {
|
||||
if !tk.IsOneOf(termSymbols) {
|
||||
var symDesc string
|
||||
if tk.IsSymbol(SymError) {
|
||||
if tk.IsSymbol(scan.SymError) {
|
||||
symDesc = tk.ErrorText()
|
||||
} 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 {
|
||||
err = tk.Error()
|
||||
}
|
||||
|
||||
+24
-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.
|
||||
|
||||
// plugin.go
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"os"
|
||||
"plugin"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
var pluginRegister map[string]*plugin.Plugin
|
||||
@@ -31,20 +33,20 @@ func pluginExists(name string) (exists bool) {
|
||||
func makePluginName(name string) (decorated string) {
|
||||
var template string
|
||||
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
||||
template = "expr-%s-plugin.so"
|
||||
template = "expr-%s-plugin%s"
|
||||
} else {
|
||||
template = "expr-%s-plugin.so.debug"
|
||||
template = "expr-%s-plugin%s.debug"
|
||||
}
|
||||
decorated = fmt.Sprintf(template, name)
|
||||
decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION)
|
||||
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 p *plugin.Plugin
|
||||
var sym plugin.Symbol
|
||||
var moduleName string
|
||||
var importFunc func(ExprContext)
|
||||
var importFunc func(kern.ExprContext)
|
||||
var ok bool
|
||||
|
||||
decoratedName := makePluginName(name)
|
||||
@@ -71,7 +73,7 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
|
||||
}
|
||||
if deps := *sym.(*[]string); len(deps) > 0 {
|
||||
// var count int
|
||||
if err = loadModules(dirList, deps); err != nil {
|
||||
if err = loadModules(ctx, dirList, deps); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -80,31 +82,35 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
|
||||
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)
|
||||
return
|
||||
}
|
||||
|
||||
registerPlugin(moduleName, p)
|
||||
importFunc(globalCtx)
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
importFunc(globalCtx)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func importPluginFromSearchPath(name any) (count int, err error) {
|
||||
func importPluginFromSearchPath(ctx kern.ExprContext, name any) (count int, err error) {
|
||||
var moduleSpec any
|
||||
|
||||
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
||||
var it kern.Iterator
|
||||
dirList := buildSearchDirList(ctx, "plugin", ENV_EXPR_PLUGIN_PATH)
|
||||
count = 0
|
||||
it := NewAnyIterator(name)
|
||||
if it, err = NewIterator(ctx, name, nil); err != nil {
|
||||
return
|
||||
}
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if err = importPlugin(dirList, module); err != nil {
|
||||
if err = importPlugin(ctx, dirList, module); err != nil {
|
||||
break
|
||||
}
|
||||
count++
|
||||
} 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
|
||||
}
|
||||
}
|
||||
@@ -114,10 +120,10 @@ func importPluginFromSearchPath(name any) (count int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func loadModules(dirList []string, moduleNames []string) (err error) {
|
||||
func loadModules(ctx kern.ExprContext, dirList []string, moduleNames []string) (err error) {
|
||||
for _, name := range moduleNames {
|
||||
if err1 := importPlugin(dirList, name); err1 != nil {
|
||||
if !ImportInContext(name) {
|
||||
if err1 := importPlugin(ctx, dirList, name); err1 != nil {
|
||||
if !ImportInContext(ctx, name) {
|
||||
err = err1
|
||||
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.
|
||||
|
||||
// expr project scanner.go
|
||||
package expr
|
||||
package scan
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -11,9 +11,12 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
"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
|
||||
prev *Token
|
||||
stage *Token
|
||||
@@ -23,8 +26,8 @@ type scanner struct {
|
||||
translations map[Symbol]Symbol
|
||||
}
|
||||
|
||||
func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *scanner) {
|
||||
inst = &scanner{
|
||||
func NewScanner(s io.Reader, translations map[Symbol]Symbol) (inst *Scanner) {
|
||||
inst = &Scanner{
|
||||
stream: bufio.NewReader(s),
|
||||
row: 1,
|
||||
column: 1,
|
||||
@@ -50,7 +53,11 @@ func DefaultTranslations() map[Symbol]Symbol {
|
||||
// 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 == '\n' {
|
||||
scanner.row++
|
||||
@@ -62,7 +69,7 @@ func (scanner *scanner) readChar() (ch byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) unreadChar() (err error) {
|
||||
func (scanner *Scanner) unreadChar() (err error) {
|
||||
if err = scanner.stream.UnreadByte(); err == nil {
|
||||
if scanner.column--; scanner.column == 0 {
|
||||
if scanner.row--; scanner.row == 0 {
|
||||
@@ -75,7 +82,7 @@ func (scanner *scanner) unreadChar() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) UnreadToken() (err error) {
|
||||
func (scanner *Scanner) UnreadToken() (err error) {
|
||||
if scanner.stage == nil {
|
||||
scanner.stage = scanner.current
|
||||
scanner.current = scanner.prev
|
||||
@@ -85,7 +92,7 @@ func (scanner *scanner) UnreadToken() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) lastPos() (r, c int) {
|
||||
func (scanner *Scanner) LastPos() (r, c int) {
|
||||
if scanner.prev != nil {
|
||||
r = scanner.prev.row
|
||||
c = scanner.prev.col
|
||||
@@ -93,11 +100,11 @@ func (scanner *scanner) lastPos() (r, c int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) Previous() *Token {
|
||||
func (scanner *Scanner) Previous() *Token {
|
||||
return scanner.prev
|
||||
}
|
||||
|
||||
func (scanner *scanner) Next() (tk *Token) {
|
||||
func (scanner *Scanner) Next() (tk *Token) {
|
||||
scanner.prev = scanner.current
|
||||
tk = scanner.current
|
||||
if scanner.stage != nil {
|
||||
@@ -109,7 +116,7 @@ func (scanner *scanner) Next() (tk *Token) {
|
||||
return tk
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
var ch byte
|
||||
if err := scanner.skipBlanks(); err != nil {
|
||||
return scanner.makeErrorToken(err)
|
||||
@@ -127,7 +134,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymPlusGreater, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPlus, ch)
|
||||
tk = scanner.MakeToken(SymPlus, ch)
|
||||
}
|
||||
case '-':
|
||||
if next, _ := scanner.peek(); next == '-' {
|
||||
@@ -135,7 +142,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next == '=' {
|
||||
tk = scanner.moveOn(SymMinusEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymMinus, ch)
|
||||
tk = scanner.MakeToken(SymMinus, ch)
|
||||
}
|
||||
case '*':
|
||||
if next, _ := scanner.peek(); next == '*' {
|
||||
@@ -145,7 +152,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymStarEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymStar, ch)
|
||||
tk = scanner.MakeToken(SymStar, ch)
|
||||
}
|
||||
case '/':
|
||||
if next, _ := scanner.peek(); next == '*' {
|
||||
@@ -157,11 +164,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchOnLineComment()
|
||||
} else {
|
||||
tk = scanner.makeToken(SymSlash, ch)
|
||||
tk = scanner.MakeToken(SymSlash, ch)
|
||||
}
|
||||
case '\\':
|
||||
if escape {
|
||||
tk = scanner.makeToken(SymBackSlash, ch)
|
||||
tk = scanner.MakeToken(SymBackSlash, ch)
|
||||
escape = false
|
||||
} else {
|
||||
escape = true
|
||||
@@ -172,24 +179,24 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymVertBarEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymVertBar, ch)
|
||||
tk = scanner.MakeToken(SymVertBar, ch)
|
||||
}
|
||||
case ',':
|
||||
tk = scanner.makeToken(SymComma, ch)
|
||||
tk = scanner.MakeToken(SymComma, ch)
|
||||
case '^':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymCaretEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymCaret, ch)
|
||||
tk = scanner.MakeToken(SymCaret, ch)
|
||||
}
|
||||
case ':':
|
||||
if next, _ := scanner.peek(); next == ':' {
|
||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymColon, ch)
|
||||
tk = scanner.MakeToken(SymColon, ch)
|
||||
}
|
||||
case ';':
|
||||
tk = scanner.makeToken(SymSemiColon, ch)
|
||||
tk = scanner.MakeToken(SymSemiColon, ch)
|
||||
case '.':
|
||||
//if next, _ := self.peek(); next >= '0' && next <= '9' {
|
||||
// tk = self.parseNumber(ch)
|
||||
@@ -203,29 +210,29 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk = scanner.moveOn(SymDoubleDot, ch, next)
|
||||
}
|
||||
} else {
|
||||
tk = scanner.makeToken(SymDot, ch)
|
||||
tk = scanner.MakeToken(SymDot, ch)
|
||||
}
|
||||
case '\'':
|
||||
if escape {
|
||||
tk = scanner.makeToken(SymQuote, ch)
|
||||
tk = scanner.MakeToken(SymQuote, ch)
|
||||
escape = false
|
||||
} else {
|
||||
tk = scanner.fetchString(ch)
|
||||
tk = scanner.fetchString(ch, true)
|
||||
}
|
||||
case '"':
|
||||
if escape {
|
||||
tk = scanner.makeToken(SymDoubleQuote, ch)
|
||||
tk = scanner.MakeToken(SymDoubleQuote, ch)
|
||||
escape = false
|
||||
} else {
|
||||
tk = scanner.fetchString(ch)
|
||||
tk = scanner.fetchString(ch, true)
|
||||
}
|
||||
case '`':
|
||||
tk = scanner.makeToken(SymBackTick, ch)
|
||||
tk = scanner.MakeToken(SymBackTick, ch)
|
||||
case '!':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymNotEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymExclamation, ch)
|
||||
tk = scanner.MakeToken(SymExclamation, ch)
|
||||
}
|
||||
case '?':
|
||||
if next, _ := scanner.peek(); next == '?' {
|
||||
@@ -235,7 +242,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next == '!' {
|
||||
tk = scanner.moveOn(SymQuestionExclam, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymQuestion, ch)
|
||||
tk = scanner.MakeToken(SymQuestion, ch)
|
||||
}
|
||||
case '&':
|
||||
if next, _ := scanner.peek(); next == '&' {
|
||||
@@ -243,16 +250,16 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymAmpersandEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymAmpersand, ch)
|
||||
tk = scanner.MakeToken(SymAmpersand, ch)
|
||||
}
|
||||
case '%':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymPercEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPercent, ch)
|
||||
tk = scanner.MakeToken(SymPercent, ch)
|
||||
}
|
||||
case '#':
|
||||
tk = scanner.makeToken(SymHash, ch)
|
||||
tk = scanner.MakeToken(SymHash, ch)
|
||||
case '@':
|
||||
if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
|
||||
scanner.readChar()
|
||||
@@ -265,15 +272,15 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next == '@' {
|
||||
tk = scanner.moveOn(SymDoubleAt, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymAt, ch)
|
||||
tk = scanner.MakeToken(SymAt, ch)
|
||||
}
|
||||
case '_':
|
||||
tk = scanner.makeToken(SymUndescore, ch)
|
||||
tk = scanner.MakeToken(SymUndescore, ch)
|
||||
case '=':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymDoubleEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymEqual, ch)
|
||||
tk = scanner.MakeToken(SymEqual, ch)
|
||||
}
|
||||
case '<':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
@@ -292,7 +299,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
} else if next == '+' {
|
||||
tk = scanner.moveOn(SymLessPlus, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymLess, ch)
|
||||
tk = scanner.MakeToken(SymLess, ch)
|
||||
}
|
||||
case '>':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
@@ -307,7 +314,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||
}
|
||||
} else {
|
||||
tk = scanner.makeToken(SymGreater, ch)
|
||||
tk = scanner.MakeToken(SymGreater, ch)
|
||||
}
|
||||
case '$':
|
||||
if next, _ := scanner.peek(); next == '(' {
|
||||
@@ -315,27 +322,35 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk.source += ")"
|
||||
} else if next == '$' {
|
||||
tk = scanner.moveOn(SymDoubleDollar, ch, next)
|
||||
} else if next == '{' {
|
||||
scanner.readChar()
|
||||
if tk = scanner.fetchString('}', false); tk != nil {
|
||||
tk.Sym = SymIdentifier
|
||||
}
|
||||
} else if next == '_' || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchIdentifier(next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymDollar, ch)
|
||||
tk = scanner.MakeToken(SymDollar, ch)
|
||||
}
|
||||
case '(':
|
||||
// if next, _ := scanner.peek(); next == ')' {
|
||||
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
|
||||
// } else {
|
||||
tk = scanner.makeToken(SymOpenRound, ch)
|
||||
tk = scanner.MakeToken(SymOpenRound, ch)
|
||||
// }
|
||||
case ')':
|
||||
tk = scanner.makeToken(SymClosedRound, ch)
|
||||
tk = scanner.MakeToken(SymClosedRound, ch)
|
||||
case '[':
|
||||
tk = scanner.makeToken(SymOpenSquare, ch)
|
||||
tk = scanner.MakeToken(SymOpenSquare, ch)
|
||||
case ']':
|
||||
tk = scanner.makeToken(SymClosedSquare, ch)
|
||||
tk = scanner.MakeToken(SymClosedSquare, ch)
|
||||
case '{':
|
||||
tk = scanner.makeToken(SymOpenBrace, ch)
|
||||
tk = scanner.MakeToken(SymOpenBrace, ch)
|
||||
case '}':
|
||||
tk = scanner.makeToken(SymClosedBrace, ch)
|
||||
tk = scanner.MakeToken(SymClosedBrace, ch)
|
||||
case '~':
|
||||
tk = scanner.makeToken(SymTilde, ch)
|
||||
tk = scanner.MakeToken(SymTilde, ch)
|
||||
case 0:
|
||||
if escape {
|
||||
tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
|
||||
@@ -362,7 +377,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) sync(err error) error {
|
||||
func (scanner *Scanner) sync(err error) error {
|
||||
if err == nil {
|
||||
err = scanner.unreadChar()
|
||||
}
|
||||
@@ -385,7 +400,7 @@ func isHexDigit(ch byte) bool {
|
||||
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 digitType string
|
||||
firstCh = currentFirstCh
|
||||
@@ -424,7 +439,7 @@ func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
func (scanner *Scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
var err error
|
||||
var ch byte
|
||||
var sym Symbol = SymInteger
|
||||
@@ -495,12 +510,12 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
if sym == SymFloat {
|
||||
value, err = strconv.ParseFloat(txt, 64)
|
||||
} else if sym == SymFraction {
|
||||
value, err = makeGeneratingFraction(txt)
|
||||
value, err = kern.MakeGeneratingFraction(txt)
|
||||
} else {
|
||||
value, err = strconv.ParseInt(txt, numBase, 64)
|
||||
}
|
||||
if err == nil {
|
||||
tk = scanner.makeValueToken(sym, txt, value)
|
||||
tk = scanner.MakeValueToken(sym, txt, value)
|
||||
} else {
|
||||
tk = scanner.makeErrorToken(err)
|
||||
}
|
||||
@@ -508,7 +523,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
||||
func (scanner *Scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
||||
var err error
|
||||
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() {
|
||||
@@ -525,14 +540,14 @@ func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
||||
if sym, ok := keywords[uptxt]; ok {
|
||||
tk = scanner.makeKeywordToken(sym, uptxt)
|
||||
} else if uptxt == `TRUE` {
|
||||
tk = scanner.makeValueToken(SymBool, txt, true)
|
||||
tk = scanner.MakeValueToken(SymBool, txt, true)
|
||||
} else if uptxt == `FALSE` {
|
||||
tk = scanner.makeValueToken(SymBool, txt, false)
|
||||
tk = scanner.MakeValueToken(SymBool, txt, false)
|
||||
} else if ch, _ := scanner.peek(); ch == '(' {
|
||||
scanner.readChar()
|
||||
tk = scanner.makeValueToken(SymFuncCall, txt+"(", txt)
|
||||
tk = scanner.MakeValueToken(SymFuncCall, txt+"(", txt)
|
||||
} else {
|
||||
tk = scanner.makeValueToken(SymIdentifier, txt, txt)
|
||||
tk = scanner.MakeValueToken(SymIdentifier, txt, txt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,20 +567,20 @@ func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchBlockComment() *Token {
|
||||
func (scanner *Scanner) fetchBlockComment() *Token {
|
||||
return scanner.fetchUntil(SymComment, false, '*', '/')
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchOnLineComment() *Token {
|
||||
func (scanner *Scanner) fetchOnLineComment() *Token {
|
||||
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 ch byte
|
||||
var sb strings.Builder
|
||||
var value string
|
||||
ring := NewByteSlider(len(endings))
|
||||
ring := util.NewByteSlider(len(endings))
|
||||
endReached := false
|
||||
for ch, err = scanner.readChar(); err == nil && !endReached; {
|
||||
sb.WriteByte(ch)
|
||||
@@ -583,14 +598,14 @@ func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (
|
||||
}
|
||||
|
||||
if endReached {
|
||||
tk = scanner.makeValueToken(sym, "", value)
|
||||
tk = scanner.MakeValueToken(sym, "", value)
|
||||
} else {
|
||||
tk = scanner.makeErrorToken(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||
func (scanner *Scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
|
||||
var err error
|
||||
var ch, prev byte
|
||||
var sb strings.Builder
|
||||
@@ -628,12 +643,16 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||
}
|
||||
} else {
|
||||
txt := sb.String()
|
||||
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
|
||||
if addQuote {
|
||||
tk = scanner.MakeValueToken(SymString, `"`+txt+`"`, txt)
|
||||
} else {
|
||||
tk = scanner.MakeValueToken(SymString, txt, txt)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) peek() (next byte, err error) {
|
||||
func (scanner *Scanner) peek() (next byte, err error) {
|
||||
var one []byte
|
||||
if one, err = scanner.stream.Peek(1); err == nil {
|
||||
next = one[0]
|
||||
@@ -641,7 +660,7 @@ func (scanner *scanner) peek() (next byte, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) skipBlanks() (err error) {
|
||||
func (scanner *Scanner) skipBlanks() (err error) {
|
||||
var one []byte
|
||||
for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) {
|
||||
scanner.readChar()
|
||||
@@ -649,7 +668,7 @@ func (scanner *scanner) skipBlanks() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) translate(sym Symbol) Symbol {
|
||||
func (scanner *Scanner) translate(sym Symbol) Symbol {
|
||||
if scanner.translations != nil {
|
||||
if translatedSym, ok := scanner.translations[sym]; ok {
|
||||
return translatedSym
|
||||
@@ -658,7 +677,7 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
|
||||
return sym
|
||||
}
|
||||
|
||||
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||
func (scanner *Scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
// for i := 1; i < len(chars); i++ {
|
||||
if len(chars) > 1 {
|
||||
@@ -668,26 +687,26 @@ func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
|
||||
func (scanner *Scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
|
||||
func (scanner *Scanner) MakeToken(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
||||
func (scanner *Scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
||||
func (scanner *Scanner) MakeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
||||
tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) makeErrorToken(err error) *Token {
|
||||
func (scanner *Scanner) makeErrorToken(err error) *Token {
|
||||
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.
|
||||
|
||||
// Symbol.go
|
||||
package expr
|
||||
package scan
|
||||
|
||||
type Symbol int16
|
||||
|
||||
@@ -119,6 +119,11 @@ const (
|
||||
SymKwPlugin
|
||||
SymKwIn
|
||||
SymKwInclude
|
||||
SymKwMap
|
||||
SymKwFilter
|
||||
SymKwDigest
|
||||
SymKwGroupBy
|
||||
SymKwJoin
|
||||
SymKwNil
|
||||
SymKwUnset
|
||||
)
|
||||
@@ -135,9 +140,14 @@ func init() {
|
||||
"FUNC": SymKwFunc,
|
||||
"IN": SymKwIn,
|
||||
"INCLUDE": SymKwInclude,
|
||||
"MAP": SymKwMap,
|
||||
"FILTER": SymKwFilter,
|
||||
"NOT": SymKwNot,
|
||||
"OR": SymKwOr,
|
||||
"NIL": SymKwNil,
|
||||
"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.
|
||||
|
||||
// t_scanner_test.go
|
||||
package expr
|
||||
package scan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
func TestScanner(t *testing.T) {
|
||||
@@ -60,7 +62,7 @@ func TestScanner(t *testing.T) {
|
||||
/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil},
|
||||
/* 39 */ {`"string"`, SymString, "string", 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 {
|
||||
@@ -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.
|
||||
|
||||
// op-registry.go
|
||||
package expr
|
||||
// term-constructor-registry.go
|
||||
package scan
|
||||
|
||||
const initialRegistryCapacity = 10
|
||||
|
||||
type termContructor func(tk *Token) *term
|
||||
type termContructor func(tk *Token) *Term
|
||||
|
||||
var constructorRegistry map[Symbol]termContructor = nil
|
||||
|
||||
func registerTermConstructor(sym Symbol, constructor termContructor) {
|
||||
func RegisterTermConstructor(sym Symbol, constructor termContructor) {
|
||||
if constructorRegistry == nil {
|
||||
constructorRegistry = make(map[Symbol]termContructor, initialRegistryCapacity)
|
||||
}
|
||||
constructorRegistry[sym] = constructor
|
||||
}
|
||||
|
||||
func newTerm(tk *Token) (inst *term) {
|
||||
func NewTerm(tk *Token) (inst *Term) {
|
||||
if constructorRegistry != nil {
|
||||
if construct, exists := constructorRegistry[tk.Sym]; exists {
|
||||
inst = construct(tk)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user