Compare commits

...

43 Commits

Author SHA1 Message Date
camoroso e6844ad1e8 alias operators: '<<' same as '<+', '>>' same as '+>'. Insert and append operation optimized with linked lists 2026-05-16 17:18:23 +02:00
camoroso 3a4e217891 kern/fraction-type.go: changed some syntax 2026-05-13 08:01:18 +02:00
camoroso 0f293fdbbc restructured some test files 2026-05-11 14:01:22 +02:00
camoroso 45faea7620 map, filter and cat return an iterator 2026-05-09 17:42:04 +02:00
camoroso e5a61b5638 iter-ops raised above assign; 'cat' operator returns an iterator; $$ operator supports iterators producing a list of items 2026-05-09 12:15:59 +02:00
camoroso 5285b61320 'join' operator renamed as 'cat' 2026-05-09 10:34:46 +02:00
camoroso 5950630cf7 Doc: added more details and new functions and operators (iterator operators) 2026-05-08 15:17:24 +02:00
camoroso 78e431f2b9 forgotten changes to builtin-os-file-iter.go 2026-05-08 11:07:53 +02:00
camoroso c7dce8288f file iterators refactored 2026-05-08 11:03:16 +02:00
camoroso c10053253c os file builtins refactored with the package 'file' 2026-05-08 10:16:03 +02:00
camoroso a3c7cf2efa iter-iter.go: check ctx against nil on creation 2026-05-08 10:14:07 +02:00
camoroso dfa1491093 kern/common-errors.go: new error function ErrFuncInvalidArg() 2026-05-08 10:10:28 +02:00
camoroso 99c1adc434 copyright header updated to 2026 2026-05-06 09:27:42 +02:00
camoroso 5585b496fb iter-iter: changed item operation from function and args to a list of expressions 2026-05-06 04:04:08 +02:00
camoroso acd4f8487d new iter-iter iterator and kern.func-info module 2026-05-05 20:38:30 +02:00
camoroso 7f34ccf955 moved scanner sources to package 'scan' 2026-05-03 14:19:17 +02:00
camoroso f63ff5953e graph.go: conditioned compilation by 'graph' tag 2026-05-03 07:14:30 +02:00
camoroso b9d37a5b4c kern/compare.go: added copyright comment 2026-05-03 07:13:39 +02:00
camoroso 23b8eec74a builtin-base: removed useless function unset(). See UNSET operator 2026-05-03 06:46:51 +02:00
camoroso bb6b6d17ec operator-map.go: return nil on error 2026-05-03 06:30:56 +02:00
camoroso 53acacbadf kern/common-errors.go: little changes to ErrExpectedGot() and ErrInvalidParameterValue() 2026-05-03 06:30:00 +02:00
camoroso 2ebc52891c Iterator operator: automatic temporary variables _index and _count changed with '__' and '_#' respectively. Note that, sinc '#' is not an identifier allowed char, '_#' requires himBHsnotation: -cover 2026-05-02 15:06:12 +02:00
camoroso 3b2ef7927b new operator 'groupby' 2026-05-02 14:53:19 +02:00
camoroso d5ced343c4 kern/compare.go: new function Equal(a,b) that returns true if a and b have the same value 2026-05-02 14:45:05 +02:00
camoroso 3ac8cab275 enhanced ending operator detection 2026-05-02 09:54:24 +02:00
camoroso 6c5e9db34b int-itrator: new iterator over integer ranges 2026-05-01 17:23:06 +02:00
camoroso 78871641d0 t_common_test.go: Error messages also contains the name of the section introduced by special symbol >>> 2026-05-01 17:17:40 +02:00
camoroso dacbec677a iterator interface chenged index and count members from int to tint 64 2026-05-01 17:15:18 +02:00
camoroso 75ed88915d ast.go, pasrse.go: fixed the recover routine 2026-05-01 17:03:44 +02:00
camoroso f2d1f23774 ast.go: added recover from panic in ast.Eval() 2026-05-01 07:25:05 +02:00
camoroso edd90054d7 parser.go: added recover from panic in parser.Parse() 2026-05-01 07:24:41 +02:00
camoroso 610e2df5f5 operator-dot: added support to access dict value by exprssion; example: D.key, D."key", D.expr 2026-04-30 07:06:20 +02:00
camoroso 32c0b45255 kern/dict-type.go: added GetItem() 2026-04-30 07:04:03 +02:00
camoroso 75a3f220df parser: token '()' returns error 2026-04-30 07:03:28 +02:00
camoroso 116b54836f token.go: little change to ErrorExpectedGotStringWithPrefix() 2026-04-30 07:01:02 +02:00
camoroso 8787973de0 util files moved form kern to util package 2026-04-29 11:03:36 +02:00
camoroso 4d910dd069 moved a subset of source file to the kern package 2026-04-27 19:43:37 +02:00
camoroso f100adead3 increased test coverage and splitted some utility functions by OS 2026-04-26 06:16:43 +02:00
camoroso 7d2cf1e687 new operator 'join' 2026-04-25 07:00:30 +02:00
camoroso 49728307f3 t_operator_test.go: added a test for the digest operator 2026-04-25 06:33:35 +02:00
camoroso 20dc502438 new operator 'digest' 2026-04-25 06:25:39 +02:00
camoroso ce7bfc5e3f operator-map/filter.go: added deletion of temporary variables '_index' and '_count' 2026-04-25 06:24:28 +02:00
camoroso 6e98bdd16b new operator 'filter' 2026-04-24 22:42:46 +02:00
153 changed files with 7172 additions and 3867 deletions
-130
View File
@@ -1,130 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// ast.go
package expr
import (
"strings"
)
//-------- ast
type ast struct {
forest []*term
root *term
}
func NewAst() *ast {
return &ast{}
}
func (expr *ast) TypeName() string {
return "Expression"
}
func (expr *ast) ToForest() {
if expr.root != nil {
if expr.forest == nil {
expr.forest = make([]*term, 0)
}
expr.forest = append(expr.forest, expr.root)
expr.root = nil
}
}
func (expr *ast) String() string {
var sb strings.Builder
if expr.root == nil {
sb.WriteString("(nil)")
} else {
expr.root.toString(&sb)
}
return sb.String()
}
func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens {
if _, err = expr.addToken(tk); err != nil {
break
}
}
return
}
// func (expr *ast) addToken(tk *Token) (err error) {
// _, err = expr.addToken2(tk)
// return
// }
func (expr *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil {
err = expr.addTerm(t)
} else {
err = tk.Errorf("unexpected token %q", tk.String())
}
return
}
func (expr *ast) addTerm(node *term) (err error) {
if expr.root == nil {
expr.root = node
} else {
expr.root, err = expr.insert(expr.root, node)
}
return
}
func (expr *ast) insert(tree, node *term) (root *term, err error) {
if tree.getPriority() < node.getPriority() {
root = tree
if tree.isComplete() {
var subRoot *term
last := tree.removeLastChild()
if subRoot, err = expr.insert(last, node); err == nil {
subRoot.setParent(tree)
}
} else {
node.setParent(tree)
}
} else if !node.isLeaf() {
root = node
tree.setParent(node)
} else {
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
}
return
}
func (expr *ast) Finish() {
if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
expr.root = expr.forest[len(expr.forest)-1]
expr.forest = expr.forest[0 : len(expr.forest)-1]
}
}
func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
expr.Finish()
if expr.root != nil {
// initDefaultVars(ctx)
if expr.forest != nil {
for _, root := range expr.forest {
if result, err = root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result)
} else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break
}
}
}
if err == nil {
if result, err = expr.root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result)
}
}
// } else {
// err = errors.New("empty expression")
}
return
}
+17 -13
View File
@@ -1,23 +1,27 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
-23
View File
@@ -1,23 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bind-go-function.go
package expr
// ---- Linking with Go functions
type golangFunctor struct {
baseFunctor
f FuncTemplate
}
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f}
}
func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+119 -110
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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,168 +149,174 @@ 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
}
func setFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var varName string
var 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 {
if result, ok = args[kern.ParamValue]; ok {
ctx.GetParent().UnsafeSetVar(varName, result)
} else {
err = ErrWrongParamType(name, ParamValue, TypeAny, args[ParamValue])
err = kern.ErrWrongParamType(name, kern.ParamValue, kern.TypeAny, args[kern.ParamValue])
}
return
}
func unsetFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var varName string
var ok bool
func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var ord int
if varName, ok = args[ParamName].(string); !ok {
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
if n, ok := args[kern.ParamValue].(byte); ok {
ord = int(n)
} else if n, ok := args[kern.ParamValue].(int64); ok {
ord = int(n)
} else if n, ok := args[kern.ParamValue].(int); ok {
ord = n
} else {
ctx.GetParent().DeleteVar(varName)
result = nil
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
}
if ord < 0 || ord > 255 {
err = kern.ErrFuncInvalidArg(name, fmt.Sprintf("character code must be in range 0-255, got %d", ord))
} else {
result = string(rune(ord))
}
return
}
//// import
func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{
NewFuncParam(ParamValue),
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
anyParams := []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamValue),
}
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)
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", 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)),
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", NewGolangFunctor(setFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamName),
NewFuncParam(ParamValue),
ctx.RegisterFunc("set", kern.NewGolangFunctor(setFunc), kern.TypeAny, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamName),
kern.NewFuncParam(kern.ParamValue),
})
ctx.RegisterFunc("unset", NewGolangFunctor(unsetFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamName),
NewFuncParam(ParamValue),
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamValue),
})
}
+14 -12
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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),
})
}
+21 -28
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-iterator.go
@@ -7,6 +7,8 @@ package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
const (
@@ -15,24 +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 args[iterParamOperator] != nil {
if op, ok = args[iterParamOperator].(Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
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
}
@@ -48,10 +50,10 @@ 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 params map[string]any
@@ -62,25 +64,16 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
if it, op, err = parseRunArgs(localCtx, args); err != nil {
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 op != nil {
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
if success, ok = ToBool(v); !success || !ok {
if success, ok = kern.ToBool(v); !success || !ok {
break
}
}
@@ -99,11 +92,11 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)),
})
}
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const fileByteIteratorType = "fileByteIterator"
type fileFileByteIterator struct {
fileIterBase
b byte
}
func newFileByteIterator(r *file.Reader, autoClose bool) *fileFileByteIterator {
return &fileFileByteIterator{
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
b: 0}
}
func (it *fileFileByteIterator) TypeName() string {
return fileByteIteratorType
}
func (it *fileFileByteIterator) String() string {
return it.repr(fileByteIteratorType)
}
func (it *fileFileByteIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.b, err = it.reader.ReadByte(); err == nil {
it.increment()
item = it.b
} else if it.autoClose {
it.Clean()
}
return
}
func (it *fileFileByteIterator) Current() (item any, err error) {
item = it.b
return
}
func (it *fileFileByteIterator) Reset() (err error) {
if err = it.reader.Reset(); err == nil {
it.reset()
it.b = 0
}
return
}
func (it *fileFileByteIterator) Clean() (err error) {
if it.reader.Valid() {
if err = it.reader.GetFile().Close(); err == nil {
it.reader = nil
}
}
it.reset()
it.b = 0
return
}
func (it *fileFileByteIterator) 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 fileByteIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *file.Reader
var invalidFileHandle any
var autoClose bool
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
if handle != nil {
result = newFileByteIterator(handle, autoClose)
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const fileLineIteratorType = "fileLineIterator"
type fileFileLineIterator struct {
fileIterBase
line string
}
func newFileLineIterator(r *file.Reader, autoClose bool) *fileFileLineIterator {
return &fileFileLineIterator{
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
line: "",
}
}
func (it *fileFileLineIterator) TypeName() string {
return fileLineIteratorType
}
func (it *fileFileLineIterator) String() string {
return it.repr(fileLineIteratorType)
}
func (it *fileFileLineIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.line, err = it.reader.ReadString('\n'); err == nil {
it.increment()
item = it.line[0 : len(it.line)-1]
} else if it.autoClose {
it.Clean()
}
return
}
func (it *fileFileLineIterator) Current() (item any, err error) {
if len(it.line) > 0 {
item = it.line[0 : len(it.line)-1]
}
return
}
func (it *fileFileLineIterator) Reset() (err error) {
if err = it.reader.Reset(); err == nil {
it.reset()
it.line = ""
}
return
}
func (it *fileFileLineIterator) Clean() (err error) {
if it.reader != nil {
if err = it.reader.Close(); err == nil {
it.reader = nil
}
}
it.reset()
it.line = ""
return
}
func (it *fileFileLineIterator) 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 fileLineIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *file.Reader
var invalidFileHandle any
var autoClose bool
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
if handle != nil {
result = newFileLineIterator(handle, autoClose)
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
+33 -111
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
@@ -6,144 +6,66 @@ package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const paramHandleOrPath = "handle-or-path"
const fileReadTextIteratorType = "fileReadTextIterator"
type fileReadTextIterator struct {
osReader *osReader
index int
count int
line string
type fileIterBase struct {
reader *file.Reader
index int64
count int64
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() int {
func (it *fileIterBase) 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() int {
func (it *fileIterBase) 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 = ""
func (it *fileIterBase) HasOperation(name string) bool {
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
}
func (it *fileIterBase) reset() {
it.index = -1
it.count = 0
}
func (it *fileIterBase) increment() {
it.index++
it.count++
}
func (it *fileIterBase) repr(typeName string) string {
if it.reader.Valid() {
return fmt.Sprintf("$(%s@%q)", typeName, it.reader.GetName())
}
return
return fmt.Sprintf("$(%s@<nil>)", typeName)
}
func (it *fileReadTextIterator) HasOperation(name string) bool {
return slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName}, name)
}
func initFileHandle(ctx kern.ExprContext, name string, args map[string]any) (handle *file.Reader, invalidFileHandle any, autoClose bool, err error) {
var ok bool
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 NextName:
v, err = it.Next()
case ResetName:
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
v, err = it.Current()
case CountName:
v = it.count
default:
err = errNoOperation(name)
}
return
}
func fileReadIteratorFunc(ctx 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 handle, ok = args[paramHandleOrPath].(*file.Reader); !ok {
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
var handleAny any
if handleAny, err = openFileFunc(ctx, name, map[string]any{ParamFilepath: fileName}); err != nil {
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
return
}
if handleAny != nil {
handle = handleAny.(*osReader)
handle = handleAny.(*file.Reader)
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")
// }
+76 -104
View File
@@ -1,58 +1,21 @@
// 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
package expr
import (
"bufio"
"fmt"
"io"
"os"
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const (
osLimitCh = "limitCh"
)
type osHandle interface {
getFile() *os.File
}
type osWriter struct {
fh *os.File
writer *bufio.Writer
}
func (h *osWriter) TypeName() string {
return "osWriter"
}
func (h *osWriter) String() string {
return "writer"
}
func (h *osWriter) getFile() *os.File {
return h.fh
}
type osReader struct {
fh *os.File
reader *bufio.Reader
}
func (h *osReader) TypeName() string {
return "osReader"
}
func (h *osReader) String() string {
return "reader"
}
func (h *osReader) getFile() *os.File {
return h.fh
}
func errMissingFilePath(funcName string) error {
return fmt.Errorf("%s(): missing or invalid file path", funcName)
}
@@ -65,55 +28,58 @@ 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 {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
}
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 = file.NewReader(fh)
// }
result, err = file.OpenReader(filePath)
} else {
err = errMissingFilePath(name)
}
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 {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
}
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 = file.NewWriter(fh)
// }
result, err = file.CreateWriter(filePath)
} else {
err = errMissingFilePath(name)
}
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 {
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)}
}
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 = file.NewWriter(fh)
// }
result, err = file.AppendWriter(filePath)
} else {
err = errMissingFilePath(name)
}
return
}
func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle file.Handle
var invalidFileHandle any
var ok bool
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if fh := handle.getFile(); fh != nil {
if w, ok := handle.(*osWriter); ok {
err = w.writer.Flush()
if fh := handle.GetFile(); fh != nil {
if w, ok := handle.(*file.Writer); ok {
err = w.Flush()
}
if err == nil {
@@ -128,20 +94,21 @@ 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) {
var handle osHandle
func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle file.Handle
var invalidFileHandle any
var ok bool
if handle, ok = args[ParamHandle].(osHandle); !ok {
invalidFileHandle = args[ParamHandle]
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if w, ok := handle.(*osWriter); ok {
if v, exists := args[ParamItem]; exists {
if w, ok := handle.(*file.Writer); ok {
if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...)
// result, err = fmt.Fprint(w.writer, argv...)
result, err = w.Write(argv...)
}
} else {
invalidFileHandle = handle
@@ -154,25 +121,25 @@ 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) {
var handle osHandle
func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle file.Handle
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].(file.Handle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
if r, ok := handle.(*file.Reader); ok {
var limit byte = '\n'
var v string
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
limit = s[0]
}
v, err = r.reader.ReadString(limit)
v, err = r.ReadString(limit)
if err == io.EOF {
err = nil
}
@@ -194,20 +161,20 @@ 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) {
var handle osHandle
func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle file.Handle
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].(file.Handle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
if r, ok := handle.(*file.Reader); ok {
var b []byte
b, err = io.ReadAll(r.reader)
b, err = r.ReadAll()
result = string(b)
} else {
invalidFileHandle = handle
@@ -220,40 +187,45 @@ 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", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
NewFuncParam(paramHandleOrPath),
ctx.RegisterFunc("fileLineIterator", kern.NewGolangFunctor(fileLineIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
kern.NewFuncParam(paramHandleOrPath),
})
ctx.RegisterFunc("fileByteIterator", kern.NewGolangFunctor(fileByteIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
kern.NewFuncParam(paramHandleOrPath),
})
}
func init() {
+72 -70
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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),
})
}
+6 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
+33 -29
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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,7 +67,7 @@ func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
}
}
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone
var s string
var argAny any
@@ -76,7 +78,7 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
} else {
argAny = "default"
}
if s, err = ToGoString(argAny, "sort type"); err == nil {
if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
@@ -97,7 +99,7 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
argAny = "default"
}
if s, err = ToGoString(argAny, "iteration mode"); err == nil {
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
dictIt.iterMode = dictIterModeKeys
@@ -114,20 +116,23 @@ func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
}
}
dictIt.makeKeys(*dict, sortType)
return dictIt, err
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)
}
@@ -137,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]
@@ -186,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
@@ -202,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
}
+122 -29
View File
@@ -34,7 +34,7 @@ Expressions calculator
toc::[]
#TODO: Work in progress (last update on 2026/04/21, 6:49 p.m.)#
#TODO: Work in progress (last update on 2026/05/08)#
== Expr
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
@@ -698,9 +698,9 @@ The value on the left side of [blue]`=` must be a variable identifier or an expr
=== Selector operator [blue]`? : ::`
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
.Selector literal Syntax
.Selector literal syntax
====
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
*_selector-operator_* = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
_selector-case_ = [_match-list_] _case-value_ +
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
_item_ = _expression_ +
@@ -737,19 +737,38 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
`>>>` [blue]`10 ? {"a"} : {"b"}` +
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
==== Triple special case of the selector operator
If the _select-expression_ is a boolean expression, the selector operator can be used as a sort of _if-then-else_ statement. In this case, the first case is evaluated if the _select-expression_ is true, and the second case is evaluated if the _select-expression_ is false. In this special case, the _match-list_ of both cases must be empty.
.Example
`>>>` [blue]`(true) ? {"T"}: {"F"}` +
[green]`T` +
`>>>` [blue]`(2 > 1) ? {"a"} : {"b"}` +
[green]`a` +
`>>>` [blue]`(2 < 1) ? {"a"} : {"b"}` +
[green]`b`
[WARNING]
====
The triple special case of the selector operator is very useful, but it only works with boolean expressions.
.Example of confusion
`>>>` [blue]`int(true) ? {"T"}: {"F"}` +
[green]`F`
====
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operatand can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
The [blue]`??` operator do not change the status of the left variable.
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
The [blue]`?=` assigns the calculated value of the right expression to the variable on the left side.
The third one, [blue]`?!`, is the alternate operator. If the variable on the left size is not defined, it returns [blue]_nil_. Otherwise it returns the result of the expressione on the right side.
IMPORTANT: If the left variable is NOT defined, the right expression is not evaluated at all.
IMPORTANT: If the variable [blue]`?!` is NOT defined, the expression is not evaluated at all.
.Examples
`>>>` [blue]`var ?? (1+2)` +
@@ -836,8 +855,14 @@ The table below shows all supported operators by decreasing priorities.
.2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_
| [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_
.2+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
4+| _See also the table of special allocation operators below_
4+| _See also the table of special assignment operators below_
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
| [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_
| [blue]`join` | _Infix_ | _Item-joining_ | _iterable_ `join` _iterable_ -> _list_
| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_
4+| _See iterators section for examples_
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|===
@@ -921,12 +946,12 @@ _param-name_ = _identifier_
[green]`fib(n):any{}`
`>>>` [gray]_// Required and optional parameters_ +
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}` +
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}` +
[green]`measure(value, unit="meter"):any{}`
=== _Golang_ function definition
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another document that I'll write, one day, maybe.
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another documents that I'll write, one day, maybe.
=== Function calls
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
@@ -1010,7 +1035,7 @@ Clone variables are normal local variables. The only diffence will appear when t
.Example
`>>>` [blue]`f = func() { @x = 3; x = 5 }` [gray]_// f() declares two *different* local variables: ``@x`` and ``x``_ +
[green]`f():any{}` +
`>>>` [blue]`f()` [gray]_// The multi-expression (two) in f() is calculated and the last result is returned_ +
`>>>` [blue]`f()` [gray]_// The multi-expression (two expressions) in f() is calculated and the last result is returned_ +
[green]`5` +
`>>>` [blue]`x` [gray]_// The `x` variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the `@x` variable, local to f() after its termnation._ +
[green]`3`
@@ -1048,7 +1073,7 @@ Builtins activation is done by using the [blue]`BUILTIN` operator. All modules e
.Builtin activation syntax
====
*_builtin-activation_* = [blue]`BUILTIN` (_builtin-name_ | _list-of-builtin-names_) +
*_builtin-activation_* = [blue]`BUILTIN` (_builtin-name_ | _list-of-builtin-names_ | **"*"**) +
_builtin-name_ = _string_ +
_list-of-builtin-names_ = **[** _string_ { "**,**" _string_ } **]**
====
@@ -1086,9 +1111,9 @@ The "base" builtin module provides functions for type checking and type conversi
* <<_fract,fract()>>
.Other functions
* <<_char,char()>>
* <<_eval,eval()>>
* <<_set,set()>>
* <<_unset,unset()>>
* <<_var,var()>>
@@ -1100,7 +1125,9 @@ Returns _true_ if the value type of _<expr>_ is boolean, false otherwise.
`>>>` [blue]`isBool(true)` +
[green]`true` +
`>>>` [blue]`isBool(3==2)` +
[green]`true`
[green]`true` +
`>>>` [blue]`isBool(3 + 2)` +
[green]`false`
===== isDict()
Syntax: `isDict(<expr>) -> bool` +
@@ -1178,7 +1205,7 @@ Returns _true_ if the value type of _<expr>_ is fraction or int, false otherwise
===== isString()
Syntax: `isString(<expr>) -> bool` +
Returns a boolean value , false otherwise.
Returns _true_ if the value type of _<expr>_ is string, false otherwise.
.Examples
`>>>` [blue]`isString("ciao")` +
@@ -1190,7 +1217,7 @@ Returns a boolean value , false otherwise.
===== bool()
Syntax: `bool(<expr>) -> bool` +
Returns a _boolean_ value consisent to the value of the expression.
Returns a _boolean_ value consisent with the value of the expression.
.Examples
`>>>` [blue]`bool(1)` +
@@ -1295,6 +1322,16 @@ Returns a _fraction_ value consistent with the value of the expression.
`>>>` [blue]`fract(true)` +
[green]`1:1`
===== char()
Syntax: `char(<intexpr>) -> string` +
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.
.Examples
`>>>` [blue]`char(65)` +
[green]`"A"` +
`>>>` [blue]`char(97)` +
[green]`"a"`
===== eval()
Syntax: `eval(<string-expr>) -> any` +
Computes and returns the value of the [.underline]#string# expression.
@@ -1308,12 +1345,12 @@ Syntax: +
`{4sp}var(<string-expr>, <expr>) -> any` +
`{4sp}var(<string-expr>) -> any`
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.
This function allows you to define variables whose names can 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
`>>>` [blue]`var("$x", 3+9)` +
[green]`12` +
`>>>` [blue]`var("$x")` +
`>>>` [blue]`var("$"+"x")` +
[green]`12` +
`>>>` [blue]`var("gain%", var("$x"))` +
[green]`12` +
@@ -1334,26 +1371,17 @@ It is equivalent to the first form of the var() function, but it is more explici
`>>>` [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"
#to-do#
===== print()
===== println()
==== Module "import"
Module actiovation: +
Module activation: +
`{4sp}BUILTIN "import"`
===== _import()_
Syntax: +
@@ -1364,6 +1392,8 @@ Loads the multi-expression contained in the specified source and returns its val
===== _importAll()_
==== Module "iterator"
Module activation: +
`{4sp}BUILTIN "iterator"`
===== run()
Syntax: +
@@ -1372,6 +1402,9 @@ Syntax: +
Iterates over the specified iterator and applies the specified operator to the current value of the iterator.
==== Module "math.arith"
Module activation: +
`{4sp}BUILTIN "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()>>
@@ -1421,23 +1454,83 @@ Same as <<_add,add()>> but returns the product of the values of the parameters.
[green]`24`
==== Module "os.file"
Module activation: +
`{4sp}BUILTIN "os.file"`
The "os.file" module provides functions for working with files.
.File related functions
* <<_fileOpen,fileOpen()>>
* <<_fileAppend,fileAppend()>>
* <<_fileCreate,fileCreate()>>
* <<_fileClose,fileClose()>>
* <<_fileWriteText,fileWriteText()>>
* <<_fileReadText,fileReadText()>>
* <<_fileReadTextAll,fileReadTextAll()>>
.Iterator functions for files
* <<_fileByteIterator,fileByteIterator()>>
* <<_fileLineIterator,fileLineIterator()>>
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()
Syntax: +
`{4sp}fileAppend(<file-path>) -> any`
Like <<_fileCreate,fileCreate()>> but write operations happen at the end of the file.
===== 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()
#to-do#
===== fileWriteText()
#to-do#
===== fileReadText()
#to-do#
===== fileReadTextAll()
#to-do#
===== fileByteIterator()
#to-do#
===== fileLineIterator()
#to-do#
==== Module "string"
Module activation: +
`{4sp}BUILTIN "string"`
This module provides functions for working with strings.
Currently available functions:
* <<_strJoin,strJoin()>>
* <<_strSub,strSub()>>
* <<_strSplit,strSplit()>>
* <<_strTrim,strTrim()>>
* <<_strStartsWith,strStartsWith()>>
* <<_strEndsWith,strEndsWith()>>
* <<_strUpper,strUpper()>>
* <<_strLower,strLower()>>
---
===== strJoin()
Syntax: +
+322
View File
@@ -0,0 +1,322 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>file: Go Coverage Report</title>
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">git.portale-stac.it/go-pkg/expr/file/file.go (88.9%)</option>
<option value="file1">git.portale-stac.it/go-pkg/expr/file/reader.go (77.8%)</option>
<option value="file2">git.portale-stac.it/go-pkg/expr/file/writer.go (100.0%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// file.go
package file
import (
"os"
"git.portale-stac.it/go-pkg/expr/kern"
)
type Handle interface {
kern.Typer
GetFile() *os.File
GetName() string
Valid() bool
Close() error
}
type handleBase struct {
fh *os.File
}
func (h *handleBase) GetFile() *os.File <span class="cov0" title="0">{
return h.fh
}</span>
func (h *handleBase) GetName() (name string) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
name = h.fh.Name()
}</span>
<span class="cov8" title="1">return</span>
}
func (h *handleBase) Valid() bool <span class="cov8" title="1">{
return h.fh != nil
}</span>
func (h *handleBase) Close() (err error) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
err = h.fh.Close()
h.fh = nil
}</span>
<span class="cov8" title="1">return</span>
}
</pre>
<pre class="file" id="file1" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import (
"bufio"
"io"
"os"
)
type Reader struct {
// fh *os.File
handleBase
reader *bufio.Reader
}
func NewReader(fh *os.File) *Reader <span class="cov8" title="1">{
return &amp;Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
}</span>
func OpenReader(filePath string) (r *Reader, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.Open(filePath); err == nil </span><span class="cov8" title="1">{
r = NewReader(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) TypeName() string <span class="cov8" title="1">{
return "fileReader"
}</span>
func (h *Reader) String() string <span class="cov8" title="1">{
return "reader"
}</span>
func (h *Reader) Valid() bool <span class="cov8" title="1">{
return h.handleBase.Valid() &amp;&amp; h.reader != nil
}</span>
func (w *Reader) Close() (err error) <span class="cov8" title="1">{
w.reader = nil
err = w.handleBase.Close()
return
}</span>
func (h *Reader) ReadByte() (b byte, err error) <span class="cov8" title="1">{
if h.reader != nil </span><span class="cov8" title="1">{
b, err = h.reader.ReadByte()
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) ReadAll() (p []byte, err error) <span class="cov8" title="1">{
if h.reader != nil </span><span class="cov8" title="1">{
p, err = io.ReadAll(h.reader)
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) ReadString(delim byte) (s string, err error) <span class="cov0" title="0">{
if h.reader != nil </span><span class="cov0" title="0">{
s, err = h.reader.ReadString(delim)
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov0" title="0">return</span>
}
func (h *Reader) Reset() (err error) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
if _, err = h.fh.Seek(0, 0); err == nil </span><span class="cov8" title="1">{
h.reader.Reset(h.fh)
}</span>
}
<span class="cov8" title="1">return</span>
}
</pre>
<pre class="file" id="file2" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import (
"bufio"
"fmt"
"os"
)
type Writer struct {
// fh *os.File
handleBase
writer *bufio.Writer
}
func NewWriter(fh *os.File) *Writer <span class="cov8" title="1">{
return &amp;Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
}</span>
func CreateWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.Create(filePath); err == nil </span><span class="cov8" title="1">{
w = NewWriter(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func AppendWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil </span><span class="cov8" title="1">{
w = NewWriter(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) TypeName() string <span class="cov8" title="1">{
return "fileWriter"
}</span>
func (w *Writer) String() string <span class="cov8" title="1">{
return "writer"
}</span>
func (w *Writer) Valid() bool <span class="cov8" title="1">{
return w.handleBase.Valid() &amp;&amp; w.writer != nil
}</span>
func (w *Writer) Close() (err error) <span class="cov8" title="1">{
var err1 error
if w.writer != nil </span><span class="cov8" title="1">{
err1 = w.Flush()
w.writer = nil
}</span>
<span class="cov8" title="1">if err = w.handleBase.Close(); err == nil </span><span class="cov8" title="1">{
err = err1
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Flush() (err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
err = w.writer.Flush()
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Write(args ...any) (n int, err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
n, err = fmt.Fprint(w.writer, args...)
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Writef(format string, args ...any) (n int, err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
n, err = fmt.Fprintf(w.writer, format, args...)
}</span>
<span class="cov8" title="1">return</span>
}
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// file.go
package file
import (
"os"
"git.portale-stac.it/go-pkg/expr/kern"
)
type Handle interface {
kern.Typer
GetFile() *os.File
GetName() string
Valid() bool
Close() error
}
type handleBase struct {
fh *os.File
}
func (h *handleBase) GetFile() *os.File {
return h.fh
}
func (h *handleBase) GetName() (name string) {
if h.fh != nil {
name = h.fh.Name()
}
return
}
func (h *handleBase) Valid() bool {
return h.fh != nil
}
func (h *handleBase) Close() (err error) {
if h.fh != nil {
err = h.fh.Close()
h.fh = nil
}
return
}
+83
View File
@@ -0,0 +1,83 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import (
"bufio"
"io"
"os"
)
type Reader struct {
// fh *os.File
handleBase
reader *bufio.Reader
}
func NewReader(fh *os.File) *Reader {
return &Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
}
func OpenReader(filePath string) (r *Reader, err error) {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
r = NewReader(fh)
}
return
}
func (h *Reader) TypeName() string {
return "fileReader"
}
func (h *Reader) String() string {
return "reader"
}
func (h *Reader) Valid() bool {
return h.handleBase.Valid() && h.reader != nil
}
func (w *Reader) Close() (err error) {
w.reader = nil
err = w.handleBase.Close()
return
}
func (h *Reader) ReadByte() (b byte, err error) {
if h.reader != nil {
b, err = h.reader.ReadByte()
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) ReadAll() (p []byte, err error) {
if h.reader != nil {
p, err = io.ReadAll(h.reader)
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) ReadString(delim byte) (s string, err error) {
if h.reader != nil {
s, err = h.reader.ReadString(delim)
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) Reset() (err error) {
if h.fh != nil {
if _, err = h.fh.Seek(0, 0); err == nil {
h.reader.Reset(h.fh)
}
}
return
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import "testing"
func TestOpenReader(t *testing.T) {
r, err := OpenReader("t_reader_test.go")
if err != nil {
t.Fatalf("OpenReader failed: %v", err)
}
defer r.Close()
if !r.Valid() {
t.Fatal("Reader should be valid after opening")
}
if r.TypeName() != "fileReader" {
t.Fatalf("Expected TypeName 'fileReader', got '%s'", r.TypeName())
}
if r.String() != "reader" {
t.Fatalf("Expected String 'reader', got '%s'", r.String())
}
// GetName may return either "t_reader_test.go" or "./t_reader_test.go" depending on the environment
name := r.GetName()
if (name != "t_reader_test.go") && (name != "./t_reader_test.go") {
t.Fatalf("Expected GetName 't_reader_test.go' or './t_reader_test.go', got '%s'", name)
}
// Test reading a byte
b, err := r.ReadByte()
if err != nil {
t.Fatalf("ReadByte failed: %v", err)
}
if b == 0 {
t.Fatal("ReadByte should not return zero byte")
}
err = r.Reset()
if err != nil {
t.Fatalf("Reset failed: %v", err)
}
if s, err := r.ReadString('\n'); err != nil {
t.Fatalf("ReadString failed: %v", err)
} else {
t.Logf("ReadString: %s", s)
}
// Test reading all content
content, err := r.ReadAll()
if err != nil {
t.Fatalf("ReadAll failed: %v", err)
}
if len(content) == 0 {
t.Fatal("ReadAll should return non-empty content")
}
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import "testing"
func TestCreateWriter(t *testing.T) {
w, err := CreateWriter("/tmp/test_writer.txt")
if err != nil {
t.Fatalf("CreateWriter failed: %v", err)
}
defer w.Close()
if !w.Valid() {
t.Fatal("Writer should be valid after creation")
}
if w.TypeName() != "fileWriter" {
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
}
if w.String() != "writer" {
t.Fatalf("Expected String 'writer', got '%s'", w.String())
}
name := w.GetName()
if name != "/tmp/test_writer.txt" {
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
}
if n, err := w.Write("Hello, World!\n"); err != nil {
t.Fatalf("Write failed: %v", err)
} else if n != len("Hello, World!\n") {
t.Fatalf("Expected to write %d bytes, wrote %d", len("Hello, World!\n"), n)
}
if n, err := w.Writef("This is a %s.\n", "test"); err != nil {
t.Fatalf("Writef failed: %v", err)
} else if n != len("This is a test.\n") {
t.Fatalf("Expected to write %d bytes, wrote %d", len("This is a test.\n"), n)
}
}
func TestAppendWriter(t *testing.T) {
w, err := AppendWriter("/tmp/test_writer.txt")
if err != nil {
t.Fatalf("AppendWriter failed: %v", err)
}
defer w.Close()
if !w.Valid() {
t.Fatal("Writer should be valid after opening for append")
}
if w.TypeName() != "fileWriter" {
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
}
if w.String() != "writer" {
t.Fatalf("Expected String 'writer', got '%s'", w.String())
}
name := w.GetName()
if name != "/tmp/test_writer.txt" {
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import (
"bufio"
"fmt"
"os"
)
type Writer struct {
// fh *os.File
handleBase
writer *bufio.Writer
}
func NewWriter(fh *os.File) *Writer {
return &Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
}
func CreateWriter(filePath string) (w *Writer, err error) {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
w = NewWriter(fh)
}
return
}
func AppendWriter(filePath string) (w *Writer, err error) {
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil {
w = NewWriter(fh)
}
return
}
func (w *Writer) TypeName() string {
return "fileWriter"
}
func (w *Writer) String() string {
return "writer"
}
func (w *Writer) Valid() bool {
return w.handleBase.Valid() && w.writer != nil
}
func (w *Writer) Close() (err error) {
var err1 error
if w.writer != nil {
err1 = w.Flush()
w.writer = nil
}
if err = w.handleBase.Close(); err == nil {
err = err1
}
return
}
func (w *Writer) Flush() (err error) {
if w.writer != nil {
err = w.writer.Flush()
}
return
}
func (w *Writer) Write(args ...any) (n int, err error) {
if w.writer != nil {
n, err = fmt.Fprint(w.writer, args...)
}
return
}
func (w *Writer) Writef(format string, args ...any) (n int, err error) {
if w.writer != nil {
n, err = fmt.Fprintf(w.writer, format, args...)
}
return
}
-386
View File
@@ -1,386 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package expr
import (
"fmt"
"strconv"
"strings"
)
// ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
// ---- Common functor definition
type baseFunctor struct {
info ExprFunc
}
func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil {
s = functor.info.ToString(opt)
} else {
s = "func(){}"
}
return s
}
func (functor *baseFunctor) GetParams() (params []ExprFuncParam) {
if functor.info != nil {
return functor.info.Params()
} else {
return []ExprFuncParam{}
}
}
func (functor *baseFunctor) SetFunc(info ExprFunc) {
functor.info = info
}
func (functor *baseFunctor) GetFunc() ExprFunc {
return functor.info
}
func (functor *baseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters
type paramFlags uint16
const (
PfDefault paramFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
name string
flags paramFlags
defaultValue any
}
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func NewFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
func (param *funcParamInfo) Name() string {
return param.name
}
func (param *funcParamInfo) Type() string {
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
return param.defaultValue
}
// --- Functions
// funcInfo implements ExprFunc
type funcInfo struct {
name string
minArgs int
maxArgs int
functor Functor
formalParams []ExprFuncParam
returnType string
}
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
var minArgs = 0
var maxArgs = 0
for _, p := range params {
if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
}
if p.IsDefault() || p.IsOptional() {
maxArgs++
} else if maxArgs == minArgs {
minArgs++
maxArgs++
} else {
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
}
if p.IsRepeat() {
minArgs--
maxArgs = -1
}
}
info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
}
functor.SetFunc(info)
return info, nil
}
func (info *funcInfo) Params() []ExprFuncParam {
return info.formalParams
}
func (info *funcInfo) ReturnType() string {
return info.returnType
}
func (info *funcInfo) ToString(opt FmtOpt) string {
var sb strings.Builder
if len(info.Name()) == 0 {
sb.WriteString("func")
} else {
sb.WriteString(info.Name())
}
sb.WriteByte('(')
if info.formalParams != nil {
for i, p := range info.formalParams {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(p.Name())
if p.IsDefault() {
sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"')
sb.WriteString(s)
sb.WriteByte('"')
} else {
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
}
}
}
}
if info.maxArgs < 0 {
sb.WriteString(" ...")
}
sb.WriteString("):")
if len(info.returnType) > 0 {
sb.WriteString(info.returnType)
} else {
sb.WriteString(TypeAny)
}
sb.WriteString("{}")
return sb.String()
}
func (info *funcInfo) Name() string {
return info.name
}
func (info *funcInfo) MinArgs() int {
return info.minArgs
}
func (info *funcInfo) MaxArgs() int {
return info.maxArgs
}
func (info *funcInfo) Functor() Functor {
return info.functor
}
func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
for i, tree := range callTerm.children {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.compute(paramCtx); err != nil {
break
}
if paramName, namedParam := getAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, len(callTerm.children)-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
// ----- Call a function ---
func getAssignVarName(t *term) (name string, ok bool) {
if ok = t.symbol() == SymEqual; ok {
name = t.children[0].source()
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
actualParams = bindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func bindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+66 -96
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+4 -2
View File
@@ -1,6 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
//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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
+12 -9
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
@@ -83,7 +86,7 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
}
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) {
@@ -106,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
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// int-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
)
type IntIterator struct {
count int64
index int64
start int64
stop int64
step int64
}
func NewIntIterator(args []any) (it *IntIterator, err error) {
var argc int = 0
if args != nil {
argc = len(args)
}
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
if argc >= 1 {
if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil {
return
}
if argc >= 2 {
it.start = it.stop
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil {
return
}
if argc >= 3 {
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil {
return
}
} else if it.start > it.stop {
it.step = -1
}
}
}
if it.step == 0 {
err = fmt.Errorf("step cannot be zero")
return
}
if it.start < it.stop && it.step < 0 {
err = fmt.Errorf("step cannot be negative when start < stop")
return
}
if it.start > it.stop && it.step > 0 {
err = fmt.Errorf("step cannot be positive when start > stop")
return
}
it.Reset()
return
}
func (it *IntIterator) String() string {
return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step)
}
func (it *IntIterator) TypeName() string {
return "IntIterator"
}
func (it *IntIterator) HasOperation(name string) bool {
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
return yes
}
func (it *IntIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
}
func (it *IntIterator) Current() (item any, err error) {
if it.start <= it.stop {
if it.index >= it.start && it.index < it.stop {
item = it.index
} else {
err = io.EOF
}
} else {
if it.index > it.stop && it.index <= it.start {
item = it.index
} else {
err = io.EOF
}
}
return
}
func (it *IntIterator) Next() (item any, err error) {
it.index += it.step
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *IntIterator) Index() int64 {
return it.index
}
func (it *IntIterator) Count() int64 {
return it.count
}
func (it *IntIterator) Reset() error {
it.index = it.start - it.step
it.count = 0
return nil
}
func (it *IntIterator) Clean() error {
return nil
}
+25 -6
View File
@@ -1,25 +1,44 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iter-factory.go
package expr
func NewIterator(value any) (it Iterator, err error) {
import (
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
if value == nil {
return NewArrayIterator([]any{}), nil
}
switch v := value.(type) {
case *ListType:
case *kern.ListType:
it = NewListIterator(v, nil)
case *DictType:
case *kern.DictType:
it, err = NewDictIterator(v, nil)
case []any:
it = NewArrayIterator(v)
case Iterator:
it = 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)
}
+135
View File
@@ -0,0 +1,135 @@
// 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"
)
const iterIterType = "IterIter"
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) {
if ctx == nil {
err = fmt.Errorf("context is required for %s", iterIterType)
} else if it == nil {
err = fmt.Errorf("source iterator is required for %s", iterIterType)
} else {
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 iterIterType
}
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
}
-51
View File
@@ -1,51 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iterator.go
package expr
import (
// "errors"
"fmt"
)
// Operator names
const (
InitName = "init"
CleanName = "clean"
ResetName = "reset"
NextName = "next"
CurrentName = "current"
IndexName = "index"
CountName = "count"
FilterName = "filter"
MapName = "map"
KeyName = "key"
ValueName = "value"
)
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
HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error)
}
type ExtIterator interface {
Iterator
Reset() error
Clean() 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")
// }
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// bind-go-function.go
package kern
// ---- Linking with Go functions
type GolangFunctor struct {
BaseFunctor
f FuncTemplate
}
func NewGolangFunctor(f FuncTemplate) *GolangFunctor {
return &GolangFunctor{f: f}
}
func (functor *GolangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *GolangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args)
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
func IsBool(v any) (ok bool) {
_, ok = v.(bool)
return ok
}
func ToBool(v any) (b bool, ok bool) {
ok = true
switch x := v.(type) {
case string:
b = len(x) > 0
case float64:
b = x != 0.0
case int64:
b = x != 0
case bool:
b = x
default:
ok = false
}
return
}
+10 -6
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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) {
@@ -84,8 +84,12 @@ func ErrUnknownVar(funcName, varName string) error {
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
}
func ErrFuncInvalidArg(funcName, details string) error {
return fmt.Errorf("%s(): invalid argument -- %s", funcName, details)
}
// --- 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())
}
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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"
+37
View File
@@ -0,0 +1,37 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
package kern
import "reflect"
func Equal(value1, value2 any) (equal bool) {
if value1 == nil && value2 == nil {
equal = true
} else if value1 == nil || value2 == nil {
equal = false
} else if IsBool(value1) && IsBool(value2) {
equal = value1.(bool) == value2.(bool)
} else if IsList(value1) && IsList(value2) {
ls1 := value1.(*ListType)
ls2 := value2.(*ListType)
equal = ls1.Equals(*ls2)
} else if IsDict(value1) && IsDict(value2) {
d1 := value1.(*DictType)
d2 := value2.(*DictType)
equal = d1.Equals(*d2)
} else if IsInteger(value1) && IsInteger(value2) {
equal = value1.(int64) == value2.(int64)
} else if IsString(value1) && IsString(value2) {
equal = value1.(string) == value2.(string)
} else if IsFloat(value1) && IsFloat(value2) {
equal = value1.(float64) == value2.(float64)
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
if eq, err := CmpAnyFract(value1, value2); err == nil {
equal = eq == 0
}
} else if !reflect.DeepEqual(value1, value2) {
equal = false
}
return
}
@@ -1,10 +1,10 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
}
+27 -4
View File
@@ -1,8 +1,10 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
+36 -9
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
+4 -2
View File
@@ -1,14 +1,15 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
+7 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
+2 -2
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr.go
package expr
package kern
// ----Expression interface
type Expr interface {
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// float.go
package kern
func IsFloat(v any) (ok bool) {
_, ok = v.(float64)
return ok
}
func AnyFloat(v any) (float float64, ok bool) {
ok = true
switch floatval := v.(type) {
case float32:
float = float64(floatval)
case float64:
float = floatval
default:
ok = false
}
return
}
+3 -3
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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 {
+62 -38
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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,21 +33,22 @@ 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
if len(s) == 0 {
goto exit
}
if s[0] == '-' {
switch s[0] {
case '-':
sign = int64(-1)
s = s[1:]
} else if s[0] == '+' {
case '+':
s = s[1:]
}
// if strings.HasSuffix(s, "()") {
@@ -59,7 +60,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 +77,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 +104,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 +114,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 +177,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 +198,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 +221,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 +240,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 +260,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 +275,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 +304,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 +318,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 +357,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 +367,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
}
+164
View File
@@ -0,0 +1,164 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// func-info.go
package kern
import (
"fmt"
"strings"
)
// --- Functions
// FuncInfo implements expr.ExprFunc
type FuncInfo struct {
name string
minArgs int
maxArgs int
functor Functor
formalParams []ExprFuncParam
returnType string
}
func NewFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *FuncInfo, err error) {
var minArgs = 0
var maxArgs = 0
for _, p := range params {
if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
}
if p.IsDefault() || p.IsOptional() {
maxArgs++
} else if maxArgs == minArgs {
minArgs++
maxArgs++
} else {
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
}
if p.IsRepeat() {
minArgs--
maxArgs = -1
}
}
info = &FuncInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
}
functor.SetFunc(info)
return info, nil
}
func (info *FuncInfo) Params() []ExprFuncParam {
return info.formalParams
}
func (info *FuncInfo) ReturnType() string {
return info.returnType
}
func (info *FuncInfo) ToString(opt FmtOpt) string {
var sb strings.Builder
if len(info.Name()) == 0 {
sb.WriteString("func")
} else {
sb.WriteString(info.Name())
}
sb.WriteByte('(')
if info.formalParams != nil {
for i, p := range info.formalParams {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(p.Name())
if p.IsDefault() {
sb.WriteByte('=')
if s, ok := p.DefaultValue().(string); ok {
sb.WriteByte('"')
sb.WriteString(s)
sb.WriteByte('"')
} else {
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
}
}
}
}
if info.maxArgs < 0 {
sb.WriteString(" ...")
}
sb.WriteString("):")
if len(info.returnType) > 0 {
sb.WriteString(info.returnType)
} else {
sb.WriteString(TypeAny)
}
sb.WriteString("{}")
return sb.String()
}
func (info *FuncInfo) Name() string {
return info.name
}
func (info *FuncInfo) MinArgs() int {
return info.minArgs
}
func (info *FuncInfo) MaxArgs() int {
return info.maxArgs
}
func (info *FuncInfo) Functor() Functor {
return info.functor
}
func (info *FuncInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *FuncInfo) ParamSpec(paramName string) ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func (info *FuncInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
+273
View File
@@ -0,0 +1,273 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package kern
import (
"fmt"
"strconv"
)
// ---- Function templates
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
type DeepFuncTemplate func(a, b any) (eq bool, err error)
// ---- Common functor definition
type BaseFunctor struct {
info ExprFunc
}
func (functor *BaseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil {
s = functor.info.ToString(opt)
} else {
s = "func(){}"
}
return s
}
func (functor *BaseFunctor) GetParams() (params []ExprFuncParam) {
if functor.info != nil {
return functor.info.Params()
} else {
return []ExprFuncParam{}
}
}
func (functor *BaseFunctor) SetFunc(info ExprFunc) {
functor.info = info
}
func (functor *BaseFunctor) GetFunc() ExprFunc {
return functor.info
}
func (functor *BaseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters
type FuncParamFlags uint16
const (
PfDefault FuncParamFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
name string
flags FuncParamFlags
defaultValue any
}
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func NewFuncParamFlag(name string, flags FuncParamFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func NewFuncParamFlagDef(name string, flags FuncParamFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
func (param *funcParamInfo) Name() string {
return param.name
}
func (param *funcParamInfo) Type() string {
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
return param.defaultValue
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm Term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
childCount := callTerm.GetChildCount()
for i := range childCount {
tree := callTerm.GetChild(i)
// for i, tree := range callTerm.Children() {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.Compute(paramCtx); err != nil {
break
}
if paramName, namedParam := GetAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, childCount-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), childCount)
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
// func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
// passedCount := len(actualParams)
// if info.MinArgs() > passedCount {
// err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
// return
// }
// if passedCount < len(info.formalParams) {
// for _, p := range info.formalParams {
// if _, exists := actualParams[p.Name()]; !exists {
// if !p.IsDefault() {
// break
// }
// if p.IsRepeat() {
// varArgs := make([]any, 1)
// varArgs[0] = p.DefaultValue()
// actualParams[p.Name()] = varArgs
// } else {
// actualParams[p.Name()] = p.DefaultValue()
// }
// }
// }
// }
// if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
// err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
// }
// return
// }
// ----- Call a function ---
// func getAssignVarName(t *term) (name string, ok bool) {
// if ok = t.symbol() == SymEqual; ok {
// name = t.children[0].source()
// }
// return
// }
func GetAssignVarName(t Term) (name string, ok bool) {
if ok = t.IsAssign(); ok {
name = t.GetChildSource(0)
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm Term) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
actualParams = BindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func BindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iterator.go
package kern
import (
// "errors"
"fmt"
"slices"
)
// Operator names
const (
InitName = "init"
CleanName = "clean"
ResetName = "reset"
NextName = "next"
CurrentName = "current"
IndexName = "index"
CountName = "count"
FilterName = "filter"
MapName = "map"
KeyName = "key"
ValueName = "value"
)
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() int64
Count() int64
HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error)
}
type ExtIterator interface {
Iterator
Reset() error
Clean() error
}
func ErrNoOperation(name string) error {
return fmt.Errorf("no %s() function defined in the data-source", name)
}
func IsIterator(v any) (ok bool) {
_, ok = v.(Iterator)
return
}
type IteratorBase struct {
ItemIndex int64
ItemCount int64
current any
}
func (it *IteratorBase) Current() (item any, err error) {
return it.current, nil
}
func (it *IteratorBase) Index() int64 {
return it.ItemIndex
}
func (it *IteratorBase) Count() int64 {
return it.ItemCount
}
func (it *IteratorBase) HasOperation(name string) bool {
return slices.Contains([]string{NextName, IndexName, CountName, CurrentName}, name)
}
func (it *IteratorBase) Clean() (err error) {
return
}
func (it *IteratorBase) Reset() (err error) {
it.ItemIndex = -1
it.ItemCount = 0
it.current = nil
return
}
func (it *IteratorBase) Increment() {
it.ItemIndex++
it.ItemCount++
}
func (it *IteratorBase) SetCurrent(v any) {
it.current = v
}
+273
View File
@@ -0,0 +1,273 @@
// simple-list project slist.go
package kern
import (
"fmt"
)
type ListNode struct {
data any
next *ListNode
}
func (node *ListNode) Next() *ListNode {
return node.next
}
func (self *ListNode) Data() any {
return self.data
}
type LinkedList struct {
count uint32
first *ListNode
last *ListNode
}
func NewLinkedList() (list *LinkedList) {
list = &LinkedList{0, nil, nil}
return
}
func (ls *LinkedList) Len() uint32 {
return ls.count
}
func (ls *LinkedList) Empty() bool {
return ls.count == 0
}
func (ls *LinkedList) PushFront(data any) *ListNode {
ls.first = &ListNode{data, ls.first}
if ls.last == nil {
ls.last = ls.first
}
ls.count++
return ls.first
}
func (ls *LinkedList) SeqPushFront(data any) (result *LinkedList) {
switch seq := data.(type) {
case Iterator:
result = ls.IterPushFront(seq)
default:
ls.PushFront(data)
result = ls
}
return
}
func (ls *LinkedList) IterPushFront(it Iterator) *LinkedList {
for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
ls.PushFront(item)
}
return ls
}
func (ls *LinkedList) PushBack(data any) (node *ListNode) {
node = &ListNode{data, nil}
if ls.last != nil {
ls.last.next = node
}
ls.last = node
if ls.first == nil {
ls.first = node
}
ls.count++
return
}
func (ls *LinkedList) SeqPushBack(data any) (result *LinkedList) {
switch seq := data.(type) {
case Iterator:
result = ls.IterPushBack(seq)
default:
ls.PushBack(data)
result = ls
}
return
}
func (ls *LinkedList) IterPushBack(it Iterator) *LinkedList {
for item, err1 := it.Next(); err1 == nil; item, err1 = it.Next() {
ls.PushBack(item)
}
return ls
}
func (ls *LinkedList) FirstNode() *ListNode {
return ls.first
}
func (ls *LinkedList) First() (data any, err error) {
if ls.first != nil {
data = ls.first.data
} else {
err = errorListEmpty()
}
return
}
func (ls *LinkedList) LastNode() *ListNode {
return ls.last
}
func (ls *LinkedList) Last() (data interface{}, err error) {
if ls.last != nil {
data = ls.last.data
} else {
err = errorListEmpty()
}
return
}
func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
if ls.count == 0 {
err = errorListEmpty()
} else if index > ls.count {
err = errorOutOfRange()
} else if index == ls.count-1 {
node = ls.last
} else {
current := ls.first
for pos := uint32(0); pos < index; pos++ {
current = current.next
}
node = current
}
return
}
func (ls *LinkedList) At(index uint32) (data interface{}, err error) {
node, err := ls.NodeAt(index)
if err == nil {
data = node.data
}
return
}
func (ls *LinkedList) Insert(index uint32, data interface{}) *ListNode {
var prev *ListNode
prev = nil
current := ls.first
for pos := uint32(0); current != nil && pos < index; pos++ {
prev = current
current = current.next
}
node := &ListNode{data, current}
if prev != nil {
prev.next = node
}
if ls.first == current {
ls.first = node
}
if node.next == nil {
ls.last = node
}
ls.count++
return node
}
func (ls *LinkedList) PushBackStringArray(items []string) {
for _, v := range items {
ls.PushBack(v)
}
}
// type TraverseOperator func(index uint32, elem interface{}, userData interface{}) (err error)
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
// index := uint32(0)
// for current := self.first; current != nil; current = current.next {
// err = op(index, current.data, user_data)
// if err != nil {
// break
// }
// index++
// }
// return
// }
// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
// index := uint32(0)
// for current := self.first; current != nil; current = current.next {
// err = observer.Observe(current, index)
// if err != nil && abortOnError {
// break
// }
// index++
// }
// return
// }
type EqualFunc func(current, target any) bool
func (ls *LinkedList) FindFirst(eqFunc EqualFunc, targetData any) (targetNode *ListNode) {
for current := ls.first; current != nil && targetNode == nil; current = current.next {
if eqFunc(current.data, targetData) {
targetNode = current
}
}
return
}
func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNode *ListNode) {
if startNode != nil {
for current := startNode.next; current != nil && targetNode == nil; current = current.next {
if eqFunc(current.data, startNode.Data()) {
targetNode = current
}
}
}
return
}
// type DataFeeder func(user_data interface{}) interface{}
// type NodeObserver func(node *ListNode, index uint32, userData interface{})
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count uint32) {
// count = 0
// for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
// // fmt.Println("Item", count, item)
// node := self.PushBack(item)
// if observer != nil {
// observer(node, count, observerUserData)
// }
// count++
// }
// return
// }
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count uint32, err error) {
// count = 0
// // item := feeder.Next()
// for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
// if err1 != nil {
// if err == nil {
// err = err1
// }
// if abortOnError {
// break
// }
// }
// // fmt.Println("Item", count, item)
// node := self.PushBack(item)
// if observer != nil {
// observer.Observe(node, count)
// }
// count++
// }
// return
// }
func errorListEmpty() error {
return fmt.Errorf("List is empty")
}
func errorOutOfRange() error {
return fmt.Errorf("Out of range")
}
+31 -41
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
}
@@ -106,22 +104,11 @@ func (ls *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
}
}
@@ -133,8 +120,11 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls1) == len(ls2) {
answer = true
for index, i1 := range *ls1 {
// if i1 != (ls2)[index] {
if reflect.DeepEqual(i1, ls2[index]) {
// if !reflect.DeepEqual(i1, ls2[index]) {
// answer = false
// break
// }
if !Equal(i1, ls2[index]) {
answer = false
break
}
@@ -143,12 +133,12 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
return
}
func (list *ListType) indexDeepSameCmp(target any) (index int) {
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
@@ -158,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
}
@@ -173,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
}
}
@@ -198,15 +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 (list *ListType) appendItem(value any) {
*list = append(*list, value)
func (dict *ListType) AppendItem(value any) {
*dict = append(*dict, value)
}
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// number.go
package kern
import (
"fmt"
)
func IsInteger(v any) (ok bool) {
_, ok = v.(int64)
return ok
}
func IsNumber(v any) (ok bool) {
return IsFloat(v) || IsInteger(v)
}
func IsNumOrFract(v any) (ok bool) {
return IsFloat(v) || IsInteger(v) || IsFraction(v)
}
func IsNumberString(v any) (ok bool) {
return IsString(v) || IsNumber(v)
}
func NumAsFloat(v any) (f float64) {
var ok bool
if f, ok = v.(float64); !ok {
if fract, ok := v.(*FractionType); ok {
f = fract.ToFloat()
} else {
i, _ := v.(int64)
f = float64(i)
}
}
return
}
func AnyInteger(v any) (i int64, ok bool) {
ok = true
switch intval := v.(type) {
case int:
i = int64(intval)
case uint8:
i = int64(intval)
case uint16:
i = int64(intval)
case uint64:
i = int64(intval)
case uint32:
i = int64(intval)
case int8:
i = int64(intval)
case int16:
i = int64(intval)
case int32:
i = int64(intval)
case int64:
i = intval
default:
ok = false
}
return
}
func ToGoInt(value any, description string) (i int, err error) {
if valueInt64, ok := value.(int64); ok {
i = int(valueInt64)
} else if valueInt, ok := value.(int); ok {
i = valueInt
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
func ToGoInt64(value any, description string) (i int64, err error) {
if valueInt64, ok := value.(int64); ok {
i = valueInt64
} else if valueInt, ok := value.(int); ok {
i = int64(valueInt)
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
import (
"fmt"
)
func IsString(v any) (ok bool) {
_, ok = v.(string)
return ok
}
func ToGoString(value any, description string) (s string, err error) {
if s, ok := value.(string); ok {
return s, nil
} else {
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
}
return
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// term.go
package kern
import (
"fmt"
)
type Term interface {
fmt.Stringer
// Children() []Term
Source() string
GetChildCount() (count int)
GetChild(index int) Term
GetChildSource(index int) string
Compute(ctx ExprContext) (result any, err error)
IsAssign() bool
Errorf(template string, args ...any) (err error)
}
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build darwin
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-darwin.go
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build linux
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-linux.go
+1 -1
View File
@@ -1,6 +1,6 @@
//go:build windows
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-windows.go
+31 -27
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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,14 +61,14 @@ 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}
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)
}
@@ -75,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
}
@@ -102,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
@@ -126,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
View File
@@ -1,29 +1,33 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,30 +1,35 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,32 +1,37 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,33 +1,38 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,34 +1,40 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserightChilded.
// 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
View File
@@ -1,50 +1,55 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,52 +1,57 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+23 -18
View File
@@ -1,50 +1,55 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
var it Iterator
if it, err = NewIterator(childValue); err != nil {
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
}
}
@@ -60,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
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+151
View File
@@ -0,0 +1,151 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-cat.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- cat term
func newCatTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalCat,
}
}
func evalCat(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
v = newCatIterator(itLeft, itRight)
return
}
const catIteratorType = "cat"
type catIterator struct {
kern.IteratorBase
itLeft kern.Iterator
itRight kern.Iterator
itCurrent kern.Iterator
}
func newCatIterator(itLeft, itRight kern.Iterator) (it *catIterator) {
it = &catIterator{
IteratorBase: kern.IteratorBase{},
itLeft: itLeft,
itRight: itRight,
itCurrent: itLeft,
}
it.Reset()
return
}
func (it *catIterator) TypeName() string {
return catIteratorType
}
func (it *catIterator) String() string {
return fmt.Sprintf("$(%s %s %s)", it.itLeft, catIteratorType, it.itRight)
}
func (it *catIterator) Next() (item any, err error) {
if it.itCurrent == nil {
err = io.EOF
} else {
if item, err = it.itCurrent.Next(); err == nil {
it.Increment()
} else if err == io.EOF {
if it.itCurrent == it.itLeft {
it.itCurrent = it.itRight
} else {
return
}
if item, err = it.itCurrent.Next(); err == nil {
it.Increment()
} else {
it.itCurrent = nil
}
}
}
it.SetCurrent(item)
return
}
// func (it *catIterator) Reset() (err error) {
// err = it.itLeft.Reset()
// it.IteratorBase.Reset()
// return
// }
func (it *catIterator) 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 = it.Index()
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.Count()
default:
err = kern.ErrNoOperation(name)
}
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwCat, newCatTerm)
}
+41 -18
View File
@@ -1,39 +1,48 @@
// 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 (
"io"
"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 kern.ExprContext
var sourceCtx ExprContext
if len(opTerm.children) == 0 {
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
}
} else {
return
}
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] != '_' })
@@ -48,13 +57,27 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
}
v = d
}
} else if childValue != nil {
if it, ok := childValue.(kern.Iterator); ok {
var item any
values := kern.NewListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
values.AppendItem(item)
}
if err == io.EOF {
err = nil
v = values
}
} else {
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
}
} else {
err = opTerm.errIncompatibleType(childValue)
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleDollar, newContextTerm)
scan.RegisterTermConstructor(scan.SymDoubleDollar, newContextTerm)
}
+16 -11
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,38 +1,43 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-digest.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- digest term
func newDigestTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalDigest,
}
}
func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item, lastValue any
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
lastValue = nil
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
if rightValue == nil {
break
} else {
lastValue = rightValue
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = lastValue
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwDigest, newDigestTerm)
}
+40 -19
View File
@@ -1,36 +1,41 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+167
View File
@@ -0,0 +1,167 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-filter.go
package expr
import (
"fmt"
"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 leftValue 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
v = newFilerIterator(ctx, it, opTerm.Children[1])
return
}
const filterIteratorType = "filter"
type filterIterator struct {
kern.IteratorBase
ctx kern.ExprContext
itSrc kern.Iterator
expr *scan.Term
}
func newFilerIterator(ctx kern.ExprContext, itSrc kern.Iterator, filterExpr *scan.Term) (it *filterIterator) {
it = &filterIterator{
IteratorBase: kern.IteratorBase{},
ctx: ctx,
itSrc: itSrc,
expr: filterExpr,
}
it.IteratorBase.Reset()
return
}
func (it *filterIterator) TypeName() string {
return filterIteratorType
}
func (it *filterIterator) String() string {
return fmt.Sprintf("$(%s %s bool-expr)", it.itSrc, filterIteratorType)
}
func (it *filterIterator) Next() (item any, err error) {
var attempt, result any
ctx := it.ctx
for attempt, err = it.itSrc.Next(); err == nil; attempt, err = it.itSrc.Next() {
ctx.SetVar("_", attempt)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
result, err = it.expr.Compute(ctx)
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err == nil {
if success, valid := kern.ToBool(result); valid {
if success {
item = attempt
break
}
} else {
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", result, result)
}
}
if err != nil {
break
}
}
it.SetCurrent(item)
return
}
// func (it *filterIterator) Reset() (err error) {
// err = it.itLeft.Reset()
// it.IteratorBase.Reset()
// return
// }
func (it *filterIterator) 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 = it.Index()
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.Count()
default:
err = kern.ErrNoOperation(name)
}
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwFilter, newFilterTerm)
}
+23 -18
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+109
View File
@@ -0,0 +1,109 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-groupby.go
package expr
import (
"fmt"
"io"
"strconv"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/scan"
)
//-------- group by term
func newGroupByTerm(tk *scan.Token) (inst *scan.Term) {
return &scan.Term{
Tk: *tk,
Children: make([]*scan.Term, 0, 2),
Position: scan.PosInfix,
Priority: scan.PriIterOp,
EvalFunc: evalGroupBy,
}
}
func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
var sKey string
var keyByIndex bool
var ok bool
if err = opTerm.CheckOperands(); err != nil {
return
}
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
return
}
if it, ok = leftValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
}
rightTk := opTerm.Children[1].Tk
if rightTk.IsSymbol(scan.SymVariable) && rightTk.Source() == "__" {
keyByIndex = true
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
return
} else if kern.IsString(rightValue) {
sKey = rightValue.(string)
} else {
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
}
values := kern.MakeDict()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
var sItemKey string
if d, ok := item.(*kern.DictType); ok {
if keyByIndex || len(sKey) == 0 {
sItemKey = strconv.Itoa(int(it.Index()))
} else if d.HasKey(sKey) {
if keyValue, exists := d.GetItem(sKey); exists {
sItemKey = fmt.Sprintf("%v", keyValue)
} else {
sItemKey = "_"
}
} else {
sItemKey = "_"
}
} else {
sItemKey = strconv.Itoa(int(it.Index()))
}
var ls *kern.ListType
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
ls = lsAny.(*kern.ListType)
}
if ls == nil {
ls = kern.NewListA()
}
ls.AppendItem(item)
values.SetItem(sItemKey, ls)
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
scan.RegisterTermConstructor(scan.SymKwGroupBy, newGroupByTerm)
}
+23 -18
View File
@@ -1,18 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,31 +1,36 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,29 +1,34 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+98 -40
View File
@@ -1,71 +1,129 @@
// 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) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
if IsList(rightValue) {
list, _ := rightValue.(*ListType)
newList := append(ListType{leftValue}, *list...)
v = &newList
if opTerm.children[1].symbol() == SymVariable {
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
func prependToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
if list, ok := rightValue.(*kern.ListType); ok {
var it kern.Iterator
if it, ok = leftValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return
}
}
ls := kern.NewLinkedList()
ls.SeqPushBack(it)
newList := kern.ListType(nil)
if newSize := len(*list) + int(ls.Len()); newSize > cap(*list) {
newList = make([]any, 0, newSize)
for node := ls.FirstNode(); node != nil; node = node.Next() {
newList = append(newList, node.Data())
}
}
for _, item := range *list {
newList = append(newList, item)
}
result = &newList
// ***** EVENTUALMENTE ABILITARE LA MODIFICA DELLA VARIABILE
// ***** CON UN OPERATORE SPECIFICO
// if opTerm.Children[1].Symbol() == scan.SymVariable {
// ctx.UnsafeSetVar(opTerm.Children[1].Source(), result)
// }
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalAppend(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(leftValue) {
list, _ := leftValue.(*ListType)
newList := append(*list, rightValue)
v = &newList
if opTerm.children[0].symbol() == SymVariable {
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
return
}
func appendToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
if list, ok := leftValue.(*kern.ListType); ok {
var it kern.Iterator
if it, ok = rightValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, rightValue, nil); err != nil {
return
}
}
ls := kern.NewLinkedList()
ls.SeqPushBack(it)
newList := *list
if newSize := len(*list) + int(ls.Len()); newSize > cap(*list) {
newList = make([]any, 0, newSize)
for _, item := range *list {
newList = append(newList, item)
}
}
for node := ls.FirstNode(); node != nil; node = node.Next() {
newList = append(newList, node.Data())
}
result = &newList
// ***** EVENTUALMENTE ABILITARE LA MODIFICA DELLA VARIABILE
// ***** CON UN OPERATORE SPECIFICO
// if opTerm.Children[0].Symbol() == scan.SymVariable {
// ctx.UnsafeSetVar(opTerm.Children[0].Source(), result)
// }
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
return
}
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 {
return
}
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
return
}
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
// var leftValue, rightValue any
@@ -86,6 +144,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
View File
@@ -1,32 +1,37 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+23 -18
View File
@@ -1,38 +1,43 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+46 -35
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-map.go
@@ -6,59 +6,70 @@ 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 *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalMap,
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 ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it Iterator
var item any
func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
// var leftValue, rightValue any
var leftValue any
var it kern.Iterator
// var item any
var ok bool
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
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue))
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 := newListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("_index", it.Index())
ctx.SetVar("_count", it.Count())
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
values.appendItem(rightValue)
}
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = values
// values := kern.NewListA()
// for item, err = it.Next(); err == nil; item, err = it.Next() {
// ctx.SetVar("_", item)
// ctx.SetVar("__", it.Index())
// ctx.SetVar("_#", it.Count())
// if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
// values.AppendItem(rightValue)
// }
// ctx.DeleteVar("_#")
// ctx.DeleteVar("__")
// ctx.DeleteVar("_")
// if err != nil {
// break
// }
// }
// if err == io.EOF {
// err = nil
// }
// if err == nil {
// v = values
// }
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
return
}
// init
func init() {
registerTermConstructor(SymKwMap, newMapTerm)
scan.RegisterTermConstructor(scan.SymKwMap, newMapTerm)
}
+17 -12
View File
@@ -1,30 +1,35 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,93 +1,96 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,69 +1,73 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,10 +1,15 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,42 +1,47 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,27 +1,32 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}
+38 -29
View File
@@ -1,77 +1,86 @@
// 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
}
v, err = bitRightShift(opTerm, leftValue, rightValue)
if v, err = bitRightShift(opTerm, leftValue, rightValue); err != nil {
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
}
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
}
v, err = bitLeftShift(opTerm, leftValue, rightValue)
if v, err = bitLeftShift(opTerm, leftValue, rightValue); err != nil {
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
scan.RegisterTermConstructor(scan.SymDoubleGreater, newRightShiftTerm)
scan.RegisterTermConstructor(scan.SymDoubleLess, newLeftShiftTerm)
}
+29 -26
View File
@@ -1,62 +1,65 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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
View File
@@ -1,24 +1,29 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// 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)
}

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