Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a6c897268 | |||
| 9eb3e2d072 | |||
| b86ce6fe62 | |||
| 38316cec0b | |||
| ee7f488ebb | |||
| 02ea20cdb5 | |||
| 20cffbddfa | |||
| cd8f331a32 | |||
| 4b2b573420 | |||
| 7ed5a806f9 | |||
| ac5c97bfd3 | |||
| e1c24daac4 | |||
| a62f27b104 | |||
| 1055569dd6 | |||
| 1aea1c14d2 | |||
| 081395be5f | |||
| 35a599b284 | |||
| eda3037855 | |||
| 47c181546a | |||
| a8a5d6aaa6 | |||
| 84b255a51b | |||
| 9efdeffcac | |||
| d34b9d8a48 | |||
| 1bf8015da1 | |||
| 3ccbeb3978 | |||
| 0c719025cd | |||
| 08617378e0 | |||
| e6844ad1e8 | |||
| 3a4e217891 | |||
| 0f293fdbbc | |||
| 45faea7620 | |||
| e5a61b5638 | |||
| 5285b61320 | |||
| 5950630cf7 | |||
| 78e431f2b9 | |||
| c7dce8288f | |||
| c10053253c | |||
| a3c7cf2efa | |||
| dfa1491093 | |||
| 99c1adc434 | |||
| 5585b496fb | |||
| acd4f8487d | |||
| 7f34ccf955 | |||
| f63ff5953e | |||
| b9d37a5b4c | |||
| 23b8eec74a | |||
| bb6b6d17ec | |||
| 53acacbadf | |||
| 2ebc52891c | |||
| 3b2ef7927b | |||
| d5ced343c4 | |||
| 3ac8cab275 | |||
| 6c5e9db34b | |||
| 78871641d0 | |||
| dacbec677a | |||
| 75ed88915d | |||
| f2d1f23774 | |||
| edd90054d7 | |||
| 610e2df5f5 | |||
| 32c0b45255 | |||
| 75a3f220df | |||
| 116b54836f | |||
| 8787973de0 | |||
| 4d910dd069 |
@@ -1,130 +0,0 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// ast.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
//-------- ast
|
||||
|
||||
type ast struct {
|
||||
forest []*term
|
||||
root *term
|
||||
}
|
||||
|
||||
func NewAst() *ast {
|
||||
return &ast{}
|
||||
}
|
||||
|
||||
func (expr *ast) TypeName() string {
|
||||
return "Expression"
|
||||
}
|
||||
|
||||
func (expr *ast) ToForest() {
|
||||
if expr.root != nil {
|
||||
if expr.forest == nil {
|
||||
expr.forest = make([]*term, 0)
|
||||
}
|
||||
expr.forest = append(expr.forest, expr.root)
|
||||
expr.root = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (expr *ast) String() string {
|
||||
var sb strings.Builder
|
||||
if expr.root == nil {
|
||||
sb.WriteString("(nil)")
|
||||
} else {
|
||||
expr.root.toString(&sb)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (expr *ast) addTokens(tokens ...*Token) (err error) {
|
||||
for _, tk := range tokens {
|
||||
if _, err = expr.addToken(tk); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func (expr *ast) addToken(tk *Token) (err error) {
|
||||
// _, err = expr.addToken2(tk)
|
||||
// return
|
||||
// }
|
||||
|
||||
func (expr *ast) addToken(tk *Token) (t *term, err error) {
|
||||
if t = newTerm(tk); t != nil {
|
||||
err = expr.addTerm(t)
|
||||
} else {
|
||||
err = tk.Errorf("unexpected token %q", tk.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (expr *ast) addTerm(node *term) (err error) {
|
||||
if expr.root == nil {
|
||||
expr.root = node
|
||||
} else {
|
||||
expr.root, err = expr.insert(expr.root, node)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (expr *ast) insert(tree, node *term) (root *term, err error) {
|
||||
if tree.getPriority() < node.getPriority() {
|
||||
root = tree
|
||||
if tree.isComplete() {
|
||||
var subRoot *term
|
||||
last := tree.removeLastChild()
|
||||
if subRoot, err = expr.insert(last, node); err == nil {
|
||||
subRoot.setParent(tree)
|
||||
}
|
||||
} else {
|
||||
node.setParent(tree)
|
||||
}
|
||||
} else if !node.isLeaf() {
|
||||
root = node
|
||||
tree.setParent(node)
|
||||
} else {
|
||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (expr *ast) Finish() {
|
||||
if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
|
||||
expr.root = expr.forest[len(expr.forest)-1]
|
||||
expr.forest = expr.forest[0 : len(expr.forest)-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
|
||||
expr.Finish()
|
||||
|
||||
if expr.root != nil {
|
||||
// initDefaultVars(ctx)
|
||||
if expr.forest != nil {
|
||||
for _, root := range expr.forest {
|
||||
if result, err = root.compute(ctx); err == nil {
|
||||
ctx.UnsafeSetVar(ControlLastResult, result)
|
||||
} else {
|
||||
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if result, err = expr.root.compute(ctx); err == nil {
|
||||
ctx.UnsafeSetVar(ControlLastResult, result)
|
||||
}
|
||||
}
|
||||
// } else {
|
||||
// err = errors.New("empty expression")
|
||||
}
|
||||
return
|
||||
}
|
||||
+17
-13
@@ -1,23 +1,27 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// function.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
// ---- Linking with Expr functions
|
||||
type exprFunctor struct {
|
||||
baseFunctor
|
||||
params []ExprFuncParam
|
||||
expr Expr
|
||||
defCtx ExprContext
|
||||
kern.BaseFunctor
|
||||
params []kern.ExprFuncParam
|
||||
expr kern.Expr
|
||||
defCtx kern.ExprContext
|
||||
}
|
||||
|
||||
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
|
||||
func (functor *exprFunctor) GetParams() (params []kern.ExprFuncParam) {
|
||||
return functor.params
|
||||
}
|
||||
|
||||
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
||||
var defCtx ExprContext
|
||||
func newExprFunctor(e kern.Expr, params []kern.ExprFuncParam, ctx kern.ExprContext) *exprFunctor {
|
||||
var defCtx kern.ExprContext
|
||||
if ctx != nil {
|
||||
defCtx = ctx
|
||||
}
|
||||
@@ -28,17 +32,17 @@ func (functor *exprFunctor) TypeName() string {
|
||||
return "ExprFunctor"
|
||||
}
|
||||
|
||||
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
||||
func (functor *exprFunctor) GetDefinitionContext() kern.ExprContext {
|
||||
return functor.defCtx
|
||||
}
|
||||
|
||||
func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func (functor *exprFunctor) InvokeNamed(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var missing []string
|
||||
for _, p := range functor.params {
|
||||
if arg, exists := args[p.Name()]; exists {
|
||||
if funcArg, ok := arg.(Functor); ok {
|
||||
if funcArg, ok := arg.(kern.Functor); ok {
|
||||
paramSpecs := funcArg.GetParams()
|
||||
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
||||
ctx.RegisterFunc(p.Name(), funcArg, kern.TypeAny, paramSpecs)
|
||||
} else {
|
||||
ctx.UnsafeSetVar(p.Name(), arg)
|
||||
}
|
||||
@@ -51,7 +55,7 @@ func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[s
|
||||
}
|
||||
}
|
||||
if missing != nil {
|
||||
err = ErrMissingParams(name, missing)
|
||||
err = kern.ErrMissingParams(name, missing)
|
||||
} else {
|
||||
result, err = functor.expr.Eval(ctx)
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// bind-go-function.go
|
||||
package expr
|
||||
|
||||
// ---- Linking with Go functions
|
||||
type golangFunctor struct {
|
||||
baseFunctor
|
||||
f FuncTemplate
|
||||
}
|
||||
|
||||
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
|
||||
return &golangFunctor{f: f}
|
||||
}
|
||||
|
||||
func (functor *golangFunctor) TypeName() string {
|
||||
return "GoFunctor"
|
||||
}
|
||||
|
||||
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
return functor.f(ctx, name, args)
|
||||
}
|
||||
+133
-110
@@ -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,188 @@ 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
|
||||
}
|
||||
|
||||
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
list := kern.NewLinkedList()
|
||||
items := args[kern.ParamValue].([]any)
|
||||
for _, arg := range items {
|
||||
list.PushBack(arg)
|
||||
}
|
||||
result = list
|
||||
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),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("seq", kern.NewGolangFunctor(seqFunc), kern.TypeLinkedList, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+14
-12
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-fmt.go
|
||||
@@ -8,11 +8,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
func getStdout(ctx ExprContext) io.Writer {
|
||||
func getStdout(ctx kern.ExprContext) io.Writer {
|
||||
var w io.Writer
|
||||
if wany, exists := ctx.GetVar(ControlStdout); exists && wany != nil {
|
||||
if wany, exists := ctx.GetVar(kern.ControlStdout); exists && wany != nil {
|
||||
w, _ = wany.(io.Writer)
|
||||
}
|
||||
if w == nil {
|
||||
@@ -21,9 +23,9 @@ func getStdout(ctx ExprContext) io.Writer {
|
||||
return w
|
||||
}
|
||||
|
||||
func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func printFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var n int = 0
|
||||
if v, exists := args[ParamItem]; exists && v != nil {
|
||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
n, err = fmt.Fprint(getStdout(ctx), argv...)
|
||||
}
|
||||
@@ -31,9 +33,9 @@ func printFunc(ctx ExprContext, name string, args map[string]any) (result any, e
|
||||
return
|
||||
}
|
||||
|
||||
func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func printLnFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var n int = 0
|
||||
if v, exists := args[ParamItem]; exists && v != nil {
|
||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
n, err = fmt.Fprintln(getStdout(ctx), argv...)
|
||||
} else {
|
||||
@@ -43,12 +45,12 @@ func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any,
|
||||
return
|
||||
}
|
||||
|
||||
func ImportFmtFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
func ImportFmtFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("print", kern.NewGolangFunctor(printFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
|
||||
})
|
||||
ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
ctx.RegisterFunc("println", kern.NewGolangFunctor(printLnFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+20
-17
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-import.go
|
||||
@@ -7,32 +7,35 @@ package expr
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
func importFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
return importGeneral(ctx, name, args)
|
||||
}
|
||||
|
||||
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
func importAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
CtrlEnable(ctx, kern.ControlExportAll)
|
||||
return importGeneral(ctx, name, args)
|
||||
}
|
||||
|
||||
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
|
||||
if v, exists := args[ParamFilepath]; exists && v != nil {
|
||||
func importGeneral(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
dirList := buildSearchDirList(ctx, "sources", ENV_EXPR_SOURCE_PATH)
|
||||
if v, exists := args[kern.ParamFilepath]; exists && v != nil {
|
||||
argv := v.([]any)
|
||||
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) {
|
||||
func doImport(ctx kern.ExprContext, name string, dirList []string, it kern.Iterator) (result any, err error) {
|
||||
var v any
|
||||
var sourceFilepath string
|
||||
|
||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||
if err = checkStringParamExpected(name, v, it.Index()); err != nil {
|
||||
if err = checkStringParamExpected(name, v, int(it.Index())); err != nil {
|
||||
break
|
||||
}
|
||||
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
|
||||
@@ -41,10 +44,10 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
var file *os.File
|
||||
if file, err = os.Open(sourceFilepath); err == nil {
|
||||
defer file.Close()
|
||||
var expr *ast
|
||||
scanner := NewScanner(file, DefaultTranslations())
|
||||
var expr *scan.Ast
|
||||
scanner := scan.NewScanner(file, scan.DefaultTranslations())
|
||||
parser := NewParser()
|
||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymEos); err == nil {
|
||||
result, err = expr.Eval(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -64,12 +67,12 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
return
|
||||
}
|
||||
|
||||
func ImportImportFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamFilepath, PfRepeat),
|
||||
func ImportImportFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("import", kern.NewGolangFunctor(importFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
|
||||
})
|
||||
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamFilepath, PfRepeat),
|
||||
ctx.RegisterFunc("importAll", kern.NewGolangFunctor(importAllFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamFilepath, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+21
-28
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// 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
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-math-arith.go
|
||||
@@ -7,67 +7,69 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
|
||||
if !(IsNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ {
|
||||
if !(kern.IsNumber(paramValue) || kern.IsFraction(paramValue)) /*|| isList(paramValue)*/ {
|
||||
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
|
||||
funcName, paramPos+1, subPos+1, level, paramValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) {
|
||||
func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
|
||||
var sumAsFloat, sumAsFract bool
|
||||
var floatSum float64 = 0.0
|
||||
var intSum int64 = 0
|
||||
var fractSum *FractionType
|
||||
var fractSum *kern.FractionType
|
||||
var v any
|
||||
|
||||
level++
|
||||
|
||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||
if list, ok := v.(*ListType); ok {
|
||||
if list, ok := v.(*kern.ListType); ok {
|
||||
v = NewListIterator(list, nil)
|
||||
}
|
||||
if subIter, ok := v.(Iterator); ok {
|
||||
if subIter, ok := v.(kern.Iterator); ok {
|
||||
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
||||
break
|
||||
}
|
||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||
if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
|
||||
if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil {
|
||||
} else if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
|
||||
break
|
||||
}
|
||||
count++
|
||||
|
||||
if !sumAsFloat {
|
||||
if IsFloat(v) {
|
||||
if kern.IsFloat(v) {
|
||||
sumAsFloat = true
|
||||
if sumAsFract {
|
||||
floatSum = fractSum.toFloat()
|
||||
floatSum = fractSum.ToFloat()
|
||||
} else {
|
||||
floatSum = float64(intSum)
|
||||
}
|
||||
} else if !sumAsFract && isFraction(v) {
|
||||
fractSum = newFraction(intSum, 1)
|
||||
} else if !sumAsFract && kern.IsFraction(v) {
|
||||
fractSum = kern.NewFraction(intSum, 1)
|
||||
sumAsFract = true
|
||||
}
|
||||
}
|
||||
|
||||
if sumAsFloat {
|
||||
floatSum += numAsFloat(v)
|
||||
floatSum += kern.NumAsFloat(v)
|
||||
} else if sumAsFract {
|
||||
var item *FractionType
|
||||
var item *kern.FractionType
|
||||
var ok bool
|
||||
if item, ok = v.(*FractionType); !ok {
|
||||
if item, ok = v.(*kern.FractionType); !ok {
|
||||
iv, _ := v.(int64)
|
||||
item = newFraction(iv, 1)
|
||||
item = kern.NewFraction(iv, 1)
|
||||
}
|
||||
fractSum = sumFract(fractSum, item)
|
||||
fractSum = kern.SumFract(fractSum, item)
|
||||
} else {
|
||||
iv, _ := v.(int64)
|
||||
intSum += iv
|
||||
@@ -86,64 +88,64 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
||||
return
|
||||
}
|
||||
|
||||
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[ParamValue].([]any)
|
||||
func addFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[kern.ParamValue].([]any)
|
||||
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||
return
|
||||
}
|
||||
|
||||
func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) {
|
||||
func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
|
||||
var mulAsFloat, mulAsFract bool
|
||||
var floatProd float64 = 1.0
|
||||
var intProd int64 = 1
|
||||
var fractProd *FractionType
|
||||
var fractProd *kern.FractionType
|
||||
var v any
|
||||
|
||||
level++
|
||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||
if list, ok := v.(*ListType); ok {
|
||||
if list, ok := v.(*kern.ListType); ok {
|
||||
v = NewListIterator(list, nil)
|
||||
}
|
||||
if subIter, ok := v.(Iterator); ok {
|
||||
if subIter, ok := v.(kern.Iterator); ok {
|
||||
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
||||
break
|
||||
}
|
||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||
if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
|
||||
if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil {
|
||||
if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
count++
|
||||
|
||||
if !mulAsFloat {
|
||||
if IsFloat(v) {
|
||||
if kern.IsFloat(v) {
|
||||
mulAsFloat = true
|
||||
if mulAsFract {
|
||||
floatProd = fractProd.toFloat()
|
||||
floatProd = fractProd.ToFloat()
|
||||
} else {
|
||||
floatProd = float64(intProd)
|
||||
}
|
||||
} else if !mulAsFract && isFraction(v) {
|
||||
fractProd = newFraction(intProd, 1)
|
||||
} else if !mulAsFract && kern.IsFraction(v) {
|
||||
fractProd = kern.NewFraction(intProd, 1)
|
||||
mulAsFract = true
|
||||
}
|
||||
}
|
||||
|
||||
if mulAsFloat {
|
||||
floatProd *= numAsFloat(v)
|
||||
floatProd *= kern.NumAsFloat(v)
|
||||
} else if mulAsFract {
|
||||
var item *FractionType
|
||||
var item *kern.FractionType
|
||||
var ok bool
|
||||
if item, ok = v.(*FractionType); !ok {
|
||||
if item, ok = v.(*kern.FractionType); !ok {
|
||||
iv, _ := v.(int64)
|
||||
item = newFraction(iv, 1)
|
||||
item = kern.NewFraction(iv, 1)
|
||||
}
|
||||
fractProd = mulFract(fractProd, item)
|
||||
fractProd = kern.MulFract(fractProd, item)
|
||||
} else {
|
||||
iv, _ := v.(int64)
|
||||
intProd *= iv
|
||||
@@ -162,19 +164,19 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
||||
return
|
||||
}
|
||||
|
||||
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[ParamValue].([]any)
|
||||
func mulFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
argv := args[kern.ParamValue].([]any)
|
||||
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||
return
|
||||
}
|
||||
|
||||
func ImportMathFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
|
||||
func ImportMathFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("add", kern.NewGolangFunctor(addFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(0)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
|
||||
ctx.RegisterFunc("mul", kern.NewGolangFunctor(mulFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, int64(1)),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-string.go
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -15,7 +17,7 @@ const (
|
||||
)
|
||||
|
||||
// --- Start of function definitions
|
||||
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
|
||||
func doJoinStr(funcName string, sep string, it kern.Iterator) (result any, err error) {
|
||||
var sb strings.Builder
|
||||
var v any
|
||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||
@@ -25,7 +27,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
||||
if s, ok := v.(string); ok {
|
||||
sb.WriteString(s)
|
||||
} else {
|
||||
err = ErrExpectedGot(funcName, TypeString, v)
|
||||
err = kern.ErrExpectedGot(funcName, kern.TypeString, v)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -36,45 +38,45 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if sep, ok := args[ParamSeparator].(string); ok {
|
||||
if v, exists := args[ParamItem]; exists {
|
||||
func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if sep, ok := args[kern.ParamSeparator].(string); ok {
|
||||
if v, exists := args[kern.ParamItem]; exists {
|
||||
argv := v.([]any)
|
||||
if len(argv) == 1 {
|
||||
if ls, ok := argv[0].(*ListType); ok {
|
||||
if ls, ok := argv[0].(*kern.ListType); ok {
|
||||
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
||||
} else if it, ok := argv[0].(Iterator); ok {
|
||||
} else if it, ok := argv[0].(kern.Iterator); ok {
|
||||
result, err = doJoinStr(name, sep, it)
|
||||
} else if s, ok := argv[0].(string); ok {
|
||||
result = s
|
||||
} else {
|
||||
err = ErrInvalidParameterValue(name, ParamItem, v)
|
||||
err = kern.ErrInvalidParameterValue(name, kern.ParamItem, v)
|
||||
}
|
||||
} else {
|
||||
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSeparator, kern.TypeString, args[kern.ParamSeparator])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func subStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var start = 0
|
||||
var count = -1
|
||||
var source string
|
||||
var ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil {
|
||||
if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
|
||||
if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -90,29 +92,29 @@ func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any,
|
||||
return
|
||||
}
|
||||
|
||||
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func trimStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var source string
|
||||
var ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
result = strings.TrimSpace(source)
|
||||
return
|
||||
}
|
||||
|
||||
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func startsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var source, prefix string
|
||||
var ok bool
|
||||
|
||||
result = false
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if prefix, ok = args[ParamPrefix].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
|
||||
if prefix, ok = args[kern.ParamPrefix].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamPrefix, kern.TypeString, args[kern.ParamPrefix])
|
||||
}
|
||||
if strings.HasPrefix(source, prefix) {
|
||||
result = true
|
||||
@@ -125,7 +127,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
|
||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -133,18 +135,18 @@ func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (resul
|
||||
return
|
||||
}
|
||||
|
||||
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func endsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var source, suffix string
|
||||
var ok bool
|
||||
|
||||
result = false
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if suffix, ok = args[ParamSuffix].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
|
||||
if suffix, ok = args[kern.ParamSuffix].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSuffix, kern.TypeString, args[kern.ParamSuffix])
|
||||
}
|
||||
if strings.HasPrefix(source, suffix) {
|
||||
result = true
|
||||
@@ -157,7 +159,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, TypeName(targetSpec))
|
||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -165,24 +167,24 @@ func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result
|
||||
return
|
||||
}
|
||||
|
||||
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
func splitStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var source, sep string
|
||||
var count int = -1
|
||||
var parts []string
|
||||
var ok bool
|
||||
|
||||
if source, ok = args[ParamSource].(string); !ok {
|
||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
if source, ok = args[kern.ParamSource].(string); !ok {
|
||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
|
||||
if sep, ok = args[ParamSeparator].(string); !ok {
|
||||
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
|
||||
if sep, ok = args[kern.ParamSeparator].(string); !ok {
|
||||
return nil, fmt.Errorf("separator param must be string, got %s (%v)", kern.TypeName(args[kern.ParamSeparator]), args[kern.ParamSeparator])
|
||||
}
|
||||
|
||||
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt()
|
||||
if count64, ok := args[kern.ParamCount].(int64); ok { // TODO replace type assertion with toInt()
|
||||
count = int(count64)
|
||||
} else {
|
||||
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount])
|
||||
return nil, fmt.Errorf("part count must be integer, got %s (%v)", kern.TypeName(args[kern.ParamCount]), args[kern.ParamCount])
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
@@ -192,7 +194,7 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
} else {
|
||||
parts = []string{}
|
||||
}
|
||||
list := make(ListType, len(parts))
|
||||
list := make(kern.ListType, len(parts))
|
||||
for i, part := range parts {
|
||||
list[i] = part
|
||||
}
|
||||
@@ -200,20 +202,20 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
return
|
||||
}
|
||||
|
||||
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
func upperStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[kern.ParamSource].(string); ok {
|
||||
result = strings.ToUpper(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
func lowerStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[kern.ParamSource].(string); ok {
|
||||
result = strings.ToLower(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -221,46 +223,46 @@ func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
// --- End of function definitions
|
||||
|
||||
// Import above functions in the context
|
||||
func ImportStringFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSeparator),
|
||||
NewFuncParamFlag(ParamItem, PfRepeat),
|
||||
func ImportStringFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("strJoin", kern.NewGolangFunctor(joinStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSeparator),
|
||||
kern.NewFuncParamFlag(kern.ParamItem, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)),
|
||||
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
||||
ctx.RegisterFunc("strSub", kern.NewGolangFunctor(subStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParamFlagDef(kern.ParamStart, kern.PfDefault, int64(0)),
|
||||
kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""),
|
||||
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
|
||||
ctx.RegisterFunc("strSplit", kern.NewGolangFunctor(splitStrFunc), "list of "+kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParamFlagDef(kern.ParamSeparator, kern.PfDefault, ""),
|
||||
kern.NewFuncParamFlagDef(kern.ParamCount, kern.PfDefault, int64(-1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strTrim", kern.NewGolangFunctor(trimStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParam(ParamPrefix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
ctx.RegisterFunc("strStartsWith", kern.NewGolangFunctor(startsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParam(kern.ParamPrefix),
|
||||
kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
NewFuncParam(ParamSuffix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
ctx.RegisterFunc("strEndsWith", kern.NewGolangFunctor(endsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
kern.NewFuncParam(kern.ParamSuffix),
|
||||
kern.NewFuncParamFlag(strParamOther, kern.PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strUpper", kern.NewGolangFunctor(upperStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
ctx.RegisterFunc("strLower", kern.NewGolangFunctor(lowerStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamSource),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtins-register.go
|
||||
@@ -6,21 +6,23 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type builtinModule struct {
|
||||
importFunc func(ExprContext)
|
||||
importFunc func(kern.ExprContext)
|
||||
description string
|
||||
imported bool
|
||||
}
|
||||
|
||||
func newBuiltinModule(importFunc func(ExprContext), description string) *builtinModule {
|
||||
func newBuiltinModule(importFunc func(kern.ExprContext), description string) *builtinModule {
|
||||
return &builtinModule{importFunc, description, false}
|
||||
}
|
||||
|
||||
var builtinModuleRegister map[string]*builtinModule
|
||||
|
||||
func RegisterBuiltinModule(name string, importFunc func(ExprContext), description string) {
|
||||
func RegisterBuiltinModule(name string, importFunc func(kern.ExprContext), description string) {
|
||||
if builtinModuleRegister == nil {
|
||||
builtinModuleRegister = make(map[string]*builtinModule)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Builder resource file
|
||||
# Created on gio 21 mag 2026, 15:35:18, CEST
|
||||
|
||||
# Program name
|
||||
PROGRAM_NAME="ecli"
|
||||
|
||||
# Program version
|
||||
PROGRAM_VERSION="$(<version.txt)"
|
||||
|
||||
# Preset platform
|
||||
platform=("linux/amd64" "darwin/arm64")
|
||||
|
||||
#--- end of file ---
|
||||
|
||||
Executable
+199
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved.
|
||||
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
|
||||
PROGRAM_NAME=""
|
||||
PROGRAM_VERSION=""
|
||||
|
||||
function usage() {
|
||||
prog=$(basename "${0}")
|
||||
msgln "USAGE:"
|
||||
#msgln " ${prog} <program-name> <program-version> <os>/<platform>..."
|
||||
msgln " ${prog} <os>/<platform>..."
|
||||
msgln " ${prog} --local Build the local exec"
|
||||
msgln " ${prog} --init Create the resource file in the current directory"
|
||||
msgln
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
msgln "Resource file '${RESOURCE_FILE}' content:"
|
||||
cat >&2 ${RESOURCE_FILE}
|
||||
else
|
||||
msgln "Resource file '${RESOURCE_FILE}' not found"
|
||||
fi
|
||||
}
|
||||
|
||||
function msgln() {
|
||||
echo >&2 "${1}"
|
||||
}
|
||||
|
||||
function exitUsage() {
|
||||
echo >&2 "${1}"
|
||||
usage
|
||||
exit 1
|
||||
}
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# CMDLINE: help
|
||||
if [ "${1}" == "help,," ] || [ "${1,,}" == "--help" ] || [ "${1,,}" == "-h" ]; then
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
# CMDLINE: init
|
||||
if [ "${1,,}" == "--init" ]; then
|
||||
cat >"${RESOURCE_FILE}" <<eot
|
||||
# Builder resource file
|
||||
# Created on $(date)
|
||||
|
||||
# Program name
|
||||
PROGRAM_NAME="name"
|
||||
|
||||
# Program version
|
||||
PROGRAM_VERSION="version"
|
||||
|
||||
# Preset platform
|
||||
platform=("linux/amd64" "darwin/arm64")
|
||||
|
||||
#--- end of file ---
|
||||
|
||||
eot
|
||||
msgln "Resource file '${RESOURCE_FILE}' create. Edit it to set valid values."
|
||||
#${EDITOR-vi} "${RESOURCE_FILE}"
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
if ! source "${RESOURCE_FILE}"; then
|
||||
exitMsg "Can't load build resource file '${RESOURCE_FILE}'"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${PROGRAM_NAME}" ]; then
|
||||
exitUsage "Missing program name"
|
||||
fi
|
||||
|
||||
if [ -z "${PROGRAM_VERSION}" ]; then
|
||||
exitUsage "Missing program version"
|
||||
fi
|
||||
|
||||
|
||||
function getBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
count=$((count+1))
|
||||
echo >"${BUILD_REGISTER}" "${PROGRAM_VERSION} ${count}"
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
function build() {
|
||||
local target=${1} ext cmd
|
||||
IFS=/ read os cpu <<<"${p}"
|
||||
#msgln "OS=${os}; CPU=${cpu}"
|
||||
ext=""
|
||||
if [ "${os}" == 'windows' ]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
cmd="GOOS='${os}' GOARCH='${cpu}' go build -o '${PROGRAM_NAME}_v${PROGRAM_VERSION}_${os}_${cpu}${ext}'"
|
||||
eval "${cmd}"
|
||||
}
|
||||
|
||||
function buildLocal() {
|
||||
local ext cmd
|
||||
ext=""
|
||||
if [[ "${OSTYPE}" =~ win.* ]]; then
|
||||
ext=".exe"
|
||||
fi
|
||||
cmd="go build -o '${PROGRAM_NAME}${ext}'"
|
||||
eval "${cmd}"
|
||||
}
|
||||
|
||||
function gitTag() {
|
||||
local gopath gopkg mod
|
||||
local tag
|
||||
|
||||
if ! tag=$(git tag -l --sort=-version:refname "v[0-9]*.[0-9]*.[0-9]*"|head -1) || [ -z "${tag}" ]; then
|
||||
gopath=$(go env GOPATH)
|
||||
gopkg="${gopath}/pkg/mod/git.portale-stac.it/go-pkg"
|
||||
if cd "${gopkg}" 2>/dev/null; then
|
||||
mod=$(ls -1v |grep expr@|tail -1)
|
||||
tag=${mod##*@}
|
||||
cd - >/dev/null
|
||||
fi
|
||||
fi
|
||||
echo ${tag}
|
||||
}
|
||||
|
||||
function gitTagDate() {
|
||||
local tag_name=${1}
|
||||
local tag_date
|
||||
|
||||
if ! tag_date=$(git show --no-patch --format=%ci "${tag_name}") || [ -z "${tag_date}" ]; then
|
||||
tag_date="n/a"
|
||||
fi
|
||||
echo ${tag_date}
|
||||
}
|
||||
|
||||
function createVersionSource() {
|
||||
local tag tag_date
|
||||
tag=$(gitTag)
|
||||
if [ -z "${tag}" ]; then
|
||||
tag="n/a"
|
||||
else
|
||||
tag_date=$(gitTagDate "${tag}")
|
||||
fi
|
||||
|
||||
cat >version.go <<eot
|
||||
// Copyright (c) 2024-$(date +%Y) Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// version.go
|
||||
package main
|
||||
|
||||
const (
|
||||
PROGNAME = "${PROGRAM_NAME}"
|
||||
VERSION = "v${PROGRAM_VERSION}(build $(getBuildCount)),$(date +"%Y/%m/%d") (celestino.amoroso@portale-stac.it)"
|
||||
EXPR_VERSION = "${tag}"
|
||||
EXPR_DATE = "${tag_date}"
|
||||
)
|
||||
eot
|
||||
}
|
||||
|
||||
## -- TEST -- ##
|
||||
|
||||
# echo "Tag: $(gitTag)"
|
||||
# echo "Tag Date: $(gitTagDate $(gitTag))"
|
||||
# exit 0
|
||||
|
||||
## -- MAIN -- ##
|
||||
createVersionSource
|
||||
|
||||
if [ "${1}" == "--local" ]; then
|
||||
buildLocal
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ ${#} -gt 0 ]; then
|
||||
for p; do
|
||||
build "${p}"
|
||||
done
|
||||
else
|
||||
for p in ${platform[@]}; do
|
||||
build "${p}"
|
||||
done
|
||||
fi
|
||||
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
|
||||
|
||||
PASSWORD=
|
||||
GITEA_USER="camoroso"
|
||||
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||
GITEA_OWNER="go-pkg"
|
||||
GITEA_HOST="https://git.portale-stac.it"
|
||||
GITEA_BASE_PATH="api/v1/packages"
|
||||
GITEA_PKG_TYPE="generic"
|
||||
|
||||
|
||||
function msgln() {
|
||||
echo >&2 "${1}"
|
||||
}
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function readBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
else
|
||||
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||
fi
|
||||
if [ -z "${PASSWORD}" ]; then
|
||||
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
|
||||
if [ -r "${RESOURCE_FILE}" ]; then
|
||||
source "${RESOURCE_FILE}"
|
||||
else
|
||||
exitMsg "resource file '${RESOURCE_FILE}' not found"
|
||||
fi
|
||||
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
BUILD_TAG=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
exitMsg "build register file '${BUILD_REGISTER}' not found"
|
||||
fi
|
||||
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/files"
|
||||
#echo "URL: ${url}"
|
||||
#echo $(curl --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||
|
||||
declare -a files=(
|
||||
$(curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||
)
|
||||
|
||||
for name in ${files[@]}; do
|
||||
filename=${name:1:${#name}-2}
|
||||
name_terminal=${filename##*_}
|
||||
filever=${name_terminal%%.*}
|
||||
|
||||
if [ "${BUILD_TAG}" != "${PROGRAM_VERSION} ${filever}" ]; then
|
||||
msgln "Deleting ${name}"
|
||||
curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X DELETE ${GITEA_HOST}/api/packages/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${filename}
|
||||
# else
|
||||
# echo "most recent version"
|
||||
fi
|
||||
done
|
||||
|
||||
#curl --user "${GITEA_USER}:${PASSWORD}" -X GET https://git.portale-stac.it/api/v1/packages/go-pkg/generic/ecli/1.7.0/files
|
||||
@@ -0,0 +1,236 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// commands.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/utils"
|
||||
)
|
||||
|
||||
type commandFunction func(opt *Options, ctx kern.ExprContext, args []string) (err error)
|
||||
|
||||
type command struct {
|
||||
name string
|
||||
description string
|
||||
code commandFunction
|
||||
}
|
||||
|
||||
func (cmd *command) exec(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
return cmd.code(opt, ctx, args)
|
||||
}
|
||||
|
||||
type commandHandler struct {
|
||||
cmdIndex []string
|
||||
commands map[string]*command
|
||||
}
|
||||
|
||||
func NewCommandHandler() *commandHandler {
|
||||
return &commandHandler{
|
||||
cmdIndex: make([]string, 0, 20),
|
||||
commands: make(map[string]*command),
|
||||
}
|
||||
}
|
||||
|
||||
// func (h *commandHandler) setContext(ctx expr.ExprContext) {
|
||||
// h.ctx = ctx
|
||||
// }
|
||||
|
||||
func (h *commandHandler) add(name, description string, f commandFunction) {
|
||||
h.cmdIndex = append(h.cmdIndex, name)
|
||||
h.commands[name] = &command{name: name, description: description, code: f}
|
||||
}
|
||||
|
||||
func (h *commandHandler) get(cmdLine string) (cmd *command, args []string) {
|
||||
if len(cmdLine) > 0 {
|
||||
tokens := strings.Split(cmdLine, " ")
|
||||
name := tokens[0]
|
||||
args = make([]string, 0, len(tokens)-1)
|
||||
if cmd = h.commands[name]; cmd != nil && len(tokens) > 1 {
|
||||
for _, tk := range tokens[1:] {
|
||||
if tk != "" {
|
||||
args = append(args, tk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ------
|
||||
var cmdHandler *commandHandler
|
||||
|
||||
func (h *commandHandler) help() {
|
||||
fmt.Fprintln(os.Stderr, `--- REPL commands:`)
|
||||
for _, name := range h.cmdIndex {
|
||||
cmd := h.commands[name]
|
||||
fmt.Fprintf(os.Stderr, "%12s -- %s\n", cmd.name, cmd.description)
|
||||
}
|
||||
fmt.Fprint(os.Stderr, `
|
||||
--- Command line options:
|
||||
-b <builtin> Import builtin modules.
|
||||
<builtin> can be a list of module names or a glob-pattern.
|
||||
Use the special value 'all' or the pattern '*' to import all modules.
|
||||
-B, --list-builtins List all builtin module names
|
||||
-e <expression> Evaluate <expression> instead of standard-input
|
||||
-i Force REPL operation when all -e occurences have been processed
|
||||
-h, --help, help Show this help menu
|
||||
-m, --modules List all builtin modules
|
||||
--noout Disable printing of expression results
|
||||
-p Print prefix form
|
||||
-t Print tree form
|
||||
-v, --version Show program version
|
||||
`)
|
||||
|
||||
}
|
||||
|
||||
// --------
|
||||
|
||||
func cmdExit(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func cmdHelp(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
cmdHandler.help()
|
||||
return
|
||||
}
|
||||
|
||||
func cmdMultiLine(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
if opt.formOpt&kern.MultiLine == 0 {
|
||||
opt.formOpt |= kern.MultiLine
|
||||
} else {
|
||||
opt.formOpt &= ^kern.MultiLine
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdTty(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
if opt.formOpt&kern.TTY == 0 {
|
||||
opt.formOpt |= kern.TTY
|
||||
} else {
|
||||
opt.formOpt &= ^kern.TTY
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func execFile(opt *Options, ctx kern.ExprContext, fileName string) (err error) {
|
||||
var fh *os.File
|
||||
if fh, err = os.Open(fileName); err == nil {
|
||||
goBatch(opt, ctx, fh, false)
|
||||
fh.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdSource(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
var target string
|
||||
for _, arg := range args {
|
||||
if len(arg) == 0 {
|
||||
continue
|
||||
}
|
||||
// TODO migliorare questa parte: eventualmente valutare un'espressione
|
||||
if target, err = checkStringLiteral(arg); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if target, err = utils.ExpandPath(target); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
if isPattern(target) {
|
||||
var fileNames []string
|
||||
if fileNames, err = matchPathPattern(target); err == nil {
|
||||
for _, fileName := range fileNames {
|
||||
if err = execFile(opt, ctx, fileName); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = execFile(opt, ctx, target)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdModules(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
|
||||
var check rune = ' '
|
||||
if imported {
|
||||
check = '*'
|
||||
}
|
||||
fmt.Printf("%c %20q: %s\n", check, name, description)
|
||||
return true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
func cmdBase(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
if len(args) == 0 {
|
||||
fmt.Println(opt.base)
|
||||
} else if args[0] == "2" {
|
||||
opt.baseVerb = "0b%b"
|
||||
opt.base = 2
|
||||
} else if args[0] == "8" {
|
||||
opt.baseVerb = "0o%o"
|
||||
opt.base = 8
|
||||
} else if args[0] == "10" {
|
||||
opt.baseVerb = "%d"
|
||||
opt.base = 10
|
||||
} else if args[0] == "16" {
|
||||
opt.baseVerb = "0x%x"
|
||||
opt.base = 16
|
||||
} else {
|
||||
err = fmt.Errorf("invalid number base %s", args[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func cmdOutput(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||
var outputArg string
|
||||
if len(args) == 0 {
|
||||
outputArg = "status"
|
||||
} else {
|
||||
outputArg = strings.ToLower(args[0])
|
||||
}
|
||||
switch outputArg {
|
||||
case "on":
|
||||
opt.output = true
|
||||
case "off":
|
||||
opt.output = false
|
||||
case "status":
|
||||
if opt.output {
|
||||
fmt.Println("on")
|
||||
} else {
|
||||
fmt.Println("off")
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("output: unknown option %q", outputArg)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//------------------
|
||||
|
||||
func setupCommands() {
|
||||
cmdHandler = NewCommandHandler()
|
||||
cmdHandler.add("base", "Set the integer output base: 2, 8, 10, or 16", cmdBase)
|
||||
cmdHandler.add("exit", "Exit the program", cmdExit)
|
||||
cmdHandler.add("help", "Show command list", cmdHelp)
|
||||
cmdHandler.add("ml", "Enable/Disable multi-line output", cmdMultiLine)
|
||||
cmdHandler.add("mods", "List builtin modules", cmdModules)
|
||||
cmdHandler.add("output", "Enable/Disable printing expression results. Options 'on', 'off', 'status'", cmdOutput)
|
||||
cmdHandler.add("source", "Load a file as input", cmdSource)
|
||||
cmdHandler.add("tty", "Enable/Disable ansi output", cmdTty)
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
= Ecli
|
||||
Expression Calculator Interactive Tool
|
||||
:authors: Celestino Amoroso
|
||||
:email: celestino.amoroso@gmail.com
|
||||
:docinfo: shared
|
||||
:encoding: utf-8
|
||||
:toc: right
|
||||
:toclevels: 4
|
||||
:icons: font
|
||||
:icon-set: fi
|
||||
:numbered:
|
||||
:data-uri:
|
||||
:docinfo1:
|
||||
:sectlinks:
|
||||
:sectanchors:
|
||||
:source-highlighter: rouge
|
||||
:rouge-style: manni
|
||||
:stylesdir: /home/share/s3-howto/styles
|
||||
:stylesheet: adoc-colony.css
|
||||
|
||||
// Workaround to manage double-column in back-tick quotes
|
||||
:2c: ::
|
||||
// Workaround to manage double-plus in back-tick quotes
|
||||
:plusplus: ++
|
||||
// Workaround to manage asterisk in back-tick quotes
|
||||
:star: *
|
||||
|
||||
#Generated by Copilot#
|
||||
|
||||
== Overview
|
||||
|
||||
`ecli` (Expression Calculator Interactive Tool) is an interactive REPL (Read-Eval-Print Loop) application for evaluating expressions using the Expr package. It provides a powerful command-line interface for expression evaluation with support for multiple builtin modules, file operations, and interactive scripting.
|
||||
|
||||
The tool combines the expression evaluation capabilities of the Expr package with an interactive shell environment, making it ideal for:
|
||||
|
||||
- Interactive expression testing and prototyping
|
||||
- Batch expression evaluation from scripts
|
||||
- Data processing and transformation
|
||||
- Mathematical computations with fractions and complex operators
|
||||
|
||||
== Getting Started
|
||||
|
||||
=== Installation
|
||||
|
||||
To build and install `ecli`:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
cd cmd/ecli
|
||||
./build.bash
|
||||
----
|
||||
|
||||
The compiled binary will be available as `ecli` in the current directory.
|
||||
|
||||
=== Basic Usage
|
||||
|
||||
Start the interactive REPL:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli
|
||||
----
|
||||
|
||||
You'll see the prompt `>>> ` where you can enter expressions to evaluate.
|
||||
|
||||
=== Command Line Options
|
||||
|
||||
[cols="1,4", options="header"]
|
||||
|===
|
||||
| Option | Description
|
||||
|
||||
| `-e <expression>` | Evaluate an expression directly without entering REPL mode
|
||||
| `-i` | Force REPL operation after processing all `-e` options
|
||||
| `-b <builtin>` | Import builtin modules (comma-separated list, glob patterns, or 'all')
|
||||
| `-B, --list-builtins` | List all available builtin module names
|
||||
| `-m, --modules` | List all builtin modules
|
||||
| `-p` | Print expressions in prefix form
|
||||
| `-t` | Print expressions in tree form
|
||||
| `--noout` | Disable printing of expression results
|
||||
| `-h, --help` | Show help message
|
||||
| `-v, --version` | Show program version
|
||||
|===
|
||||
|
||||
== Interactive Commands
|
||||
|
||||
Within the REPL, you can use the following commands:
|
||||
|
||||
[cols="2,5", options="header"]
|
||||
|===
|
||||
| Command | Description
|
||||
|
||||
| `help` | Display available commands and command-line options
|
||||
| `exit` | Exit the REPL
|
||||
| `multiline` | Toggle multi-line input mode for complex expressions
|
||||
| `tty` | Toggle TTY mode
|
||||
| `source <file>` | Execute expressions from a file
|
||||
|===
|
||||
|
||||
== Features
|
||||
|
||||
=== Expression Evaluation
|
||||
|
||||
`ecli` supports the full expression language provided by the Expr package, including:
|
||||
|
||||
- **Arithmetic Operations**: Addition, subtraction, multiplication, division, modulo
|
||||
- **Bitwise Operations**: AND, OR, XOR, NOT, shift operations
|
||||
- **Boolean Logic**: AND, OR, NOT operations
|
||||
- **Relational Operators**: Comparison and equality operators
|
||||
- **String Operations**: Concatenation and string manipulation
|
||||
- **Iterators**: Range, list, and custom iterators
|
||||
- **Functions**: Builtin and user-defined functions
|
||||
- **Collections**: Lists, dictionaries, and linked lists
|
||||
- **Fractions**: Support for fractional arithmetic
|
||||
|
||||
=== Builtin Modules
|
||||
|
||||
`ecli` provides access to various builtin modules through the `-b` option:
|
||||
|
||||
[cols="1,4", options="header"]
|
||||
|===
|
||||
| Module | Functionality
|
||||
|
||||
| `base` | Core expression evaluation functions
|
||||
| `fmt` | String formatting and output functions
|
||||
| `string` | String manipulation functions
|
||||
| `math-arith` | Mathematical and arithmetic operations
|
||||
| `iterator` | Iterator-related functions
|
||||
| `os-file` | File I/O operations
|
||||
| `import` | Module import functionality
|
||||
|===
|
||||
|
||||
Use `-B` or `--list-builtins` to see all available modules:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli --list-builtins
|
||||
----
|
||||
|
||||
== Examples
|
||||
|
||||
=== Basic Arithmetic
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
>>> 2 + 3 * 4
|
||||
14
|
||||
>>> (2 + 3) * 4
|
||||
20
|
||||
----
|
||||
|
||||
=== String Operations
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
>>> "hello" + " " + "world"
|
||||
hello world
|
||||
----
|
||||
|
||||
=== Using Iterators
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
>>> [1, 2, 3, 4, 5] | map(. * 2)
|
||||
[2, 4, 6, 8, 10]
|
||||
----
|
||||
|
||||
=== Evaluating from Command Line
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli -e "2 + 2"
|
||||
4
|
||||
----
|
||||
|
||||
=== Loading Builtin Modules
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli -b "math-arith,string"
|
||||
----
|
||||
|
||||
=== Loading Expressions from Files
|
||||
|
||||
Inside the REPL:
|
||||
|
||||
[source]
|
||||
----
|
||||
>>> source "expressions.expr"
|
||||
----
|
||||
|
||||
Or from command line:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli -e '@include "expressions.expr"'
|
||||
----
|
||||
|
||||
== Configuration
|
||||
|
||||
=== Resource Files
|
||||
|
||||
`ecli` supports startup resource files:
|
||||
|
||||
- `.ecli.rc` - Main configuration file
|
||||
- `.ecli.rc.d/` - Directory for modular configuration files
|
||||
|
||||
These files are automatically loaded at startup if they exist in the current directory or home directory.
|
||||
|
||||
== Building from Source
|
||||
|
||||
=== Prerequisites
|
||||
|
||||
- Go 1.18 or later
|
||||
- Make or bash shell
|
||||
|
||||
=== Build Steps
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
cd cmd/ecli
|
||||
./build.bash
|
||||
----
|
||||
|
||||
=== Build Artifacts
|
||||
|
||||
The build process generates:
|
||||
|
||||
- `ecli` - The main executable
|
||||
- `version.txt` - Version information
|
||||
- Platform-specific binaries (e.g., `ecli_v1.17.0_linux_amd64`, `ecli_v1.17.0_darwin_arm64`)
|
||||
|
||||
== Advanced Usage
|
||||
|
||||
=== Multi-line Input
|
||||
|
||||
For complex expressions, toggle multi-line mode:
|
||||
|
||||
[source]
|
||||
----
|
||||
>>> multiline
|
||||
>>> result = [1, 2, 3, 4, 5]
|
||||
... | filter(. > 2)
|
||||
... | map(. * 2)
|
||||
>>> result
|
||||
[6, 8, 10]
|
||||
----
|
||||
|
||||
=== Script Execution
|
||||
|
||||
Create a file `calculations.expr`:
|
||||
|
||||
[source]
|
||||
----
|
||||
x = 10
|
||||
y = 20
|
||||
result = x + y * 2
|
||||
----
|
||||
|
||||
Execute it:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli -e '@source "calculations.expr"' -e 'result'
|
||||
----
|
||||
|
||||
=== Chaining Operations
|
||||
|
||||
[source]
|
||||
----
|
||||
>>> data = [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]
|
||||
>>> data | map(.name)
|
||||
[alice, bob]
|
||||
----
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
=== Expression Parsing Errors
|
||||
|
||||
If you encounter parsing errors, check:
|
||||
|
||||
- Bracket matching and quotation marks
|
||||
- Operator precedence
|
||||
- Variable and function names
|
||||
|
||||
=== Module Loading Issues
|
||||
|
||||
Verify available modules:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./ecli --list-builtins
|
||||
----
|
||||
|
||||
=== File Not Found Errors
|
||||
|
||||
Ensure file paths are:
|
||||
|
||||
- Properly quoted in expressions
|
||||
- Relative to the current working directory or absolute paths
|
||||
- Readable by the current user
|
||||
|
||||
== Related Documentation
|
||||
|
||||
- link:../../../README.adoc[Expr Package Documentation]
|
||||
- Expr Expression Language Syntax
|
||||
- Builtin Modules Reference
|
||||
|
||||
== Version History
|
||||
|
||||
For version information and changes, see the link:version.txt[version file].
|
||||
|
||||
== License
|
||||
|
||||
Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). All rights reserved.
|
||||
@@ -0,0 +1,15 @@
|
||||
module ecli
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
git.portale-stac.it/go-pkg/expr v0.33.0
|
||||
git.portale-stac.it/go-pkg/utils v0.3.0
|
||||
github.com/ergochat/readline v0.1.3
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
git.portale-stac.it/go-pkg/expr v0.1.0 h1:7xGEuUhdh6RRFaRbRnLVqVJBmHJWHfqjDBm2K0fIW2s=
|
||||
git.portale-stac.it/go-pkg/expr v0.1.0/go.mod h1:kUFEQkUMCJ1IiUKkL0P5/vznaAIzFI26Xf5P0rTXqR0=
|
||||
git.portale-stac.it/go-pkg/expr v0.2.0 h1:AAaVsV0uaC4EikKU91VuubIpbIN7wuya7t4avyFgg+0=
|
||||
git.portale-stac.it/go-pkg/expr v0.2.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||
git.portale-stac.it/go-pkg/expr v0.17.0 h1:4ANGwJfwJO3AmnKka4Cf1AO9/ckGLMj8RIWeoDFKawQ=
|
||||
git.portale-stac.it/go-pkg/expr v0.17.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||
git.portale-stac.it/go-pkg/expr v0.32.0 h1:ikXqHjJslIGkD79G1/51xe+c25TFi2CslJ6nu8mOuJY=
|
||||
git.portale-stac.it/go-pkg/expr v0.32.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
|
||||
git.portale-stac.it/go-pkg/expr v0.33.0 h1:GJ7PPgA1689GSC/cUWGYm08jn7qMmkp0FMQf/As5sCw=
|
||||
git.portale-stac.it/go-pkg/expr v0.33.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
|
||||
git.portale-stac.it/go-pkg/utils v0.2.0 h1:2l4IVUhElzjaIUJlahPG2DZTGb9x7OXuFTO4z1K6LmY=
|
||||
git.portale-stac.it/go-pkg/utils v0.2.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
|
||||
git.portale-stac.it/go-pkg/utils v0.3.0 h1:kCJ3+XcekV7in/SieJjiswdtJKMBS0RTJMlG2fW5mK0=
|
||||
git.portale-stac.it/go-pkg/utils v0.3.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
|
||||
github.com/ergochat/readline v0.1.0 h1:KEIiAnyH9qGZB4K8oq5mgDcExlEKwmZDcyyocgJiABc=
|
||||
github.com/ergochat/readline v0.1.0/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||
github.com/ergochat/readline v0.1.1 h1:C8Uuo3ybB23GWOt0uxmHbGzKM9owmtXary6Clrj84s0=
|
||||
github.com/ergochat/readline v0.1.1/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||
github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo=
|
||||
github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
|
||||
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
//go:build graph
|
||||
|
||||
// graph.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
)
|
||||
|
||||
func printGraph() {
|
||||
r := expr.NewExprReticle(ast)
|
||||
fmt.Println(r.String())
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"git.portale-stac.it/go-pkg/utils"
|
||||
|
||||
// https://pkg.go.dev/github.com/ergochat/readline#section-readme
|
||||
"github.com/ergochat/readline"
|
||||
)
|
||||
|
||||
const (
|
||||
intro = PROGNAME + ` -- Expressions calculator ` + VERSION + `
|
||||
Based on the Expr package ` + EXPR_VERSION + ` (` + EXPR_DATE + `)
|
||||
Type help to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
`
|
||||
mainPrompt = ">>> "
|
||||
contPrompt = "... "
|
||||
|
||||
historyFile = "~/.expr_history"
|
||||
)
|
||||
|
||||
// ------
|
||||
|
||||
func errOptValueRequired(opt string) error {
|
||||
return fmt.Errorf("option %q requires a value", opt)
|
||||
}
|
||||
|
||||
func about() string {
|
||||
return PROGNAME + " -- " + VERSION + "; Expr package " + EXPR_VERSION
|
||||
}
|
||||
|
||||
func importBuiltins(ctx kern.ExprContext, opt *Options) (err error) {
|
||||
for _, spec := range opt.builtin {
|
||||
if moduleSpec, ok := spec.(string); ok {
|
||||
if moduleSpec == "all" {
|
||||
moduleSpec = "*"
|
||||
}
|
||||
_, err = expr.ImportInContextByGlobPattern(ctx, moduleSpec)
|
||||
} else if moduleSpec, ok := spec.([]string); ok {
|
||||
notFoundList := make([]string, 0)
|
||||
for _, name := range moduleSpec {
|
||||
if !expr.ImportInContext(ctx, name) {
|
||||
notFoundList = append(notFoundList, name)
|
||||
}
|
||||
}
|
||||
if len(notFoundList) > 0 {
|
||||
err = fmt.Errorf("not found modules: %s", strings.Join(notFoundList, ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func initReadlineConfig(cfg *readline.Config) {
|
||||
if histfile, err := utils.ExpandPath(historyFile); err == nil {
|
||||
cfg.HistoryFile = histfile
|
||||
}
|
||||
cfg.Undo = true
|
||||
cfg.DisableAutoSaveHistory = true
|
||||
|
||||
}
|
||||
|
||||
func goInteractiveReadline(opt *Options, ctx kern.ExprContext, r io.Reader) {
|
||||
var sb strings.Builder
|
||||
var cfg readline.Config
|
||||
initReadlineConfig(&cfg)
|
||||
rl, err := readline.NewFromConfig(&cfg)
|
||||
if err != nil {
|
||||
goInteractive(opt, ctx, r)
|
||||
return
|
||||
}
|
||||
defer rl.Close()
|
||||
|
||||
fmt.Print(intro)
|
||||
rl.SetPrompt(mainPrompt)
|
||||
for line, err := rl.ReadLine(); err == nil; line, err = rl.ReadLine() {
|
||||
if continuation(&sb, line) {
|
||||
rl.SetPrompt(contPrompt)
|
||||
continue
|
||||
}
|
||||
|
||||
rl.SetPrompt(mainPrompt)
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
rl.SaveToHistory(source)
|
||||
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
break
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rl.SaveToHistory(source)
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, true)
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func goInteractive(opt *Options, ctx kern.ExprContext, r io.Reader) {
|
||||
var sb strings.Builder
|
||||
fmt.Print(intro)
|
||||
fmt.Print(mainPrompt)
|
||||
reader := bufio.NewReaderSize(r, 1024)
|
||||
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||
if continuation(&sb, line) {
|
||||
continue
|
||||
}
|
||||
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
// fmt.Printf("source=%q\n", source)
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, true)
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
fmt.Print(mainPrompt)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func continuation(sb *strings.Builder, line string) (cont bool) {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasSuffix(line, "\\") {
|
||||
sb.WriteString(line[0 : len(line)-1])
|
||||
cont = true
|
||||
} else if strings.HasSuffix(line, ";") {
|
||||
sb.WriteString(line)
|
||||
cont = true
|
||||
} else if len(line) > 0 {
|
||||
if scan.StringEndsWithOperator(line) {
|
||||
sb.WriteString(line)
|
||||
cont = true
|
||||
} else {
|
||||
fullInput := sb.String() + line
|
||||
if strings.Count(fullInput, "(") > strings.Count(fullInput, ")") ||
|
||||
strings.Count(fullInput, "[") > strings.Count(fullInput, "]") ||
|
||||
strings.Count(fullInput, "{") > strings.Count(fullInput, "}") {
|
||||
sb.WriteString(line)
|
||||
cont = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func goBatch(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
|
||||
var sb strings.Builder
|
||||
reader := bufio.NewReaderSize(r, 1024)
|
||||
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||
if continuation(&sb, line) {
|
||||
continue
|
||||
}
|
||||
sb.WriteString(line)
|
||||
source := strings.TrimSpace(sb.String())
|
||||
// fmt.Printf("source=%q\n", source)
|
||||
if source != "" && !strings.HasPrefix(source, "//") {
|
||||
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
r := strings.NewReader(source)
|
||||
compute(opt, ctx, r, outputEnabled)
|
||||
}
|
||||
}
|
||||
sb.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func compute(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
parser := expr.NewParser()
|
||||
|
||||
if ast, err := parser.Parse(scanner); err == nil {
|
||||
if opt.printPrefix {
|
||||
fmt.Println(ast)
|
||||
}
|
||||
if opt.printTree {
|
||||
printGraph()
|
||||
}
|
||||
|
||||
if result, err := ast.Eval(ctx); err == nil {
|
||||
if outputEnabled && opt.output {
|
||||
printResult(opt, result)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintln(os.Stderr, "Parse Error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printResult(opt *Options, result any) {
|
||||
if f, ok := result.(kern.Formatter); ok {
|
||||
fmt.Println(f.ToString(opt.formOpt))
|
||||
} else if kern.IsInteger(result) {
|
||||
fmt.Printf(opt.baseVerb, result)
|
||||
fmt.Println()
|
||||
} else if kern.IsString(result) {
|
||||
fmt.Printf("\"%s\"\n", result)
|
||||
} else {
|
||||
fmt.Println(result)
|
||||
}
|
||||
}
|
||||
|
||||
func isReaderTerminal(r io.Reader) bool {
|
||||
if fh, ok := r.(*os.File); ok {
|
||||
return utils.StreamIsTerminal(fh)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func registerLocalFunctions(ctx kern.ExprContext) {
|
||||
const (
|
||||
devParamProp = "prop"
|
||||
devParamDigits = "digits"
|
||||
)
|
||||
|
||||
aboutFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
result = about()
|
||||
return
|
||||
}
|
||||
|
||||
ctrlListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
vars := ctx.EnumVars(func(name string) bool {
|
||||
return len(name) > 0 && name[0] == '_'
|
||||
})
|
||||
result = kern.ListFromStrings(vars)
|
||||
return
|
||||
}
|
||||
|
||||
ctrlFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
varName, _ := args[devParamProp].(string)
|
||||
if len(args) == 1 {
|
||||
result = expr.GlobalCtrlGet(ctx, varName)
|
||||
} else {
|
||||
result = expr.GlobalCtrlSet(ctx, varName, args[kern.ParamValue])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
envSetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName, value string
|
||||
var ok bool
|
||||
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
|
||||
return
|
||||
}
|
||||
if value, ok = args[kern.ParamValue].(string); !ok {
|
||||
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
if err = os.Setenv(varName, value); err == nil {
|
||||
result = value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
envGetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
|
||||
return
|
||||
}
|
||||
|
||||
if result, ok = os.LookupEnv(varName); !ok {
|
||||
err = fmt.Errorf("environment variable %q does not exist", varName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
envListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
env := os.Environ()
|
||||
vars := make([]string, 0, len(env))
|
||||
for _, e := range env {
|
||||
name, _, _ := strings.Cut(e, "=")
|
||||
vars = append(vars, name)
|
||||
}
|
||||
result = kern.ListFromStrings(vars)
|
||||
return
|
||||
}
|
||||
|
||||
binFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var value, digits int64
|
||||
var ok bool
|
||||
var sb strings.Builder
|
||||
|
||||
if value, ok = args[kern.ParamValue].(int64); !ok {
|
||||
err = kern.ErrExpectedGot(name, kern.TypeInt, args[kern.ParamValue])
|
||||
return
|
||||
}
|
||||
if digits, ok = args[devParamDigits].(int64); !ok {
|
||||
err = kern.ErrExpectedGot(name, kern.TypeInt, args[devParamDigits])
|
||||
return
|
||||
}
|
||||
if digits != 64 && digits != 32 && digits != 16 && digits != 8 {
|
||||
err = fmt.Errorf("%s param allows 8, 16, 32, or 64 values only", devParamDigits)
|
||||
return
|
||||
}
|
||||
|
||||
mask := uint64(0)
|
||||
for i := 0; i < int(digits); i++ {
|
||||
mask |= (1 << i)
|
||||
}
|
||||
maskedValue := uint64(value) & mask
|
||||
// if maskedValue != uint64(value) {
|
||||
// err = fmt.Errorf("%s param (%d) is not compatible with the value (%d) of %s param", expr.ParamValue, value, digits, devParamDigits)
|
||||
// return
|
||||
// }
|
||||
|
||||
for i := int(digits) - 1; i >= 0; i-- {
|
||||
if maskedValue&(1<<i) == 0 {
|
||||
sb.WriteByte('0')
|
||||
} else {
|
||||
sb.WriteByte('1')
|
||||
}
|
||||
}
|
||||
|
||||
result = sb.String()
|
||||
return
|
||||
}
|
||||
|
||||
ctx.RegisterFunc("about", kern.NewGolangFunctor(aboutFunc), kern.TypeString, []kern.ExprFuncParam{})
|
||||
ctx.RegisterFunc("ctrlList", kern.NewGolangFunctor(ctrlListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
|
||||
ctx.RegisterFunc("ctrl", kern.NewGolangFunctor(ctrlFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(devParamProp),
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfOptional),
|
||||
})
|
||||
ctx.RegisterFunc("envSet", kern.NewGolangFunctor(envSetFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamName),
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
})
|
||||
ctx.RegisterFunc("envGet", kern.NewGolangFunctor(envGetFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamName),
|
||||
})
|
||||
ctx.RegisterFunc("envList", kern.NewGolangFunctor(envListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
|
||||
|
||||
ctx.RegisterFunc("bin", kern.NewGolangFunctor(binFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
kern.NewFuncParamFlagDef(devParamDigits, kern.PfOptional|kern.PfDefault, int64(8)),
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
setupCommands()
|
||||
opt := NewOptions()
|
||||
opt.loadRc()
|
||||
if err := opt.parseArgs(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
ctx := expr.NewSimpleStore()
|
||||
|
||||
if err := importBuiltins(ctx, opt); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
registerLocalFunctions(ctx)
|
||||
|
||||
if len(opt.expressions) == opt.rcCount || opt.forceInteractive {
|
||||
opt.expressions = append(opt.expressions, os.Stdin)
|
||||
}
|
||||
|
||||
for _, input := range opt.expressions {
|
||||
if isReaderTerminal(input) {
|
||||
goInteractiveReadline(opt, ctx, input)
|
||||
} else {
|
||||
_, enableOutput := input.(*strings.Reader)
|
||||
goBatch(opt, ctx, input, enableOutput)
|
||||
if f, ok := input.(*os.File); ok {
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: why did I added these lines?
|
||||
// if opt.output {
|
||||
// printResult(opt, ctx.GetLast())
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// match.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func matchFilePattern(dirName string, pattern string, join bool) (fileList []string, err error) {
|
||||
var entries []os.DirEntry
|
||||
if entries, err = os.ReadDir(dirName); err == nil {
|
||||
for _, entry := range entries {
|
||||
if entry.Type().IsRegular() {
|
||||
var match bool
|
||||
if match, err =filepath.Match(pattern, entry.Name()); err != nil {
|
||||
fileList = nil
|
||||
break
|
||||
}
|
||||
if match {
|
||||
if fileList == nil {
|
||||
fileList = make([]string, 0, 1)
|
||||
}
|
||||
if join {
|
||||
fileList = append(fileList, path.Join(dirName, entry.Name()))
|
||||
} else {
|
||||
fileList = append(fileList, entry.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func matchPathPattern(pathPattern string) (fileList []string, err error) {
|
||||
dirName := path.Dir(pathPattern)
|
||||
pattern := path.Base(pathPattern)
|
||||
return matchFilePattern(dirName, pattern, true)
|
||||
}
|
||||
|
||||
|
||||
func isPattern(name string) bool{
|
||||
return strings.ContainsAny(name, "*?[]")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"path"
|
||||
"slices"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsPattern(t *testing.T) {
|
||||
target := "non-pattern"
|
||||
if isPattern(target) {
|
||||
t.Errorf("%q recognized as a pattern", target)
|
||||
}
|
||||
|
||||
target = "pattern/*.expr"
|
||||
if !isPattern(target) {
|
||||
t.Errorf("%q not recognized as a pattern", target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchFilePattern(t *testing.T) {
|
||||
target := "./go.*sum"
|
||||
dirName := path.Dir(target)
|
||||
pattern := path.Base(target)
|
||||
if matchedFiles, err := matchFilePattern(dirName, pattern, true); err == nil {
|
||||
if slices.Compare(matchedFiles, []string{"go.sum", "go.work.sum"}) != 0 {
|
||||
t.Errorf("Matched file list is not correct: %v", matchedFiles)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Got error: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// options.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr"
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/utils"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
printTree bool
|
||||
printPrefix bool
|
||||
forceInteractive bool
|
||||
builtin []any
|
||||
expressions []io.Reader
|
||||
formOpt kern.FmtOpt
|
||||
baseVerb string
|
||||
base int
|
||||
output bool
|
||||
rcCount int
|
||||
}
|
||||
|
||||
func NewOptions() *Options {
|
||||
return &Options{
|
||||
expressions: make([]io.Reader, 0),
|
||||
builtin: make([]any, 0),
|
||||
formOpt: kern.Base10,
|
||||
baseVerb: "%d",
|
||||
base: 10,
|
||||
output: true,
|
||||
rcCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (opt *Options) loadRc() {
|
||||
var rcPath string
|
||||
var fh *os.File
|
||||
var err error
|
||||
|
||||
rcList := []string{
|
||||
".ecli.rc",
|
||||
"~/.ecli.rc",
|
||||
"~/.config/expr/ecli.rc",
|
||||
"~/.dev-expr.rc", // OBSOLETE, to be removed in future releases
|
||||
}
|
||||
for _, rcFile := range rcList {
|
||||
if rcPath, err = utils.ExpandPath(rcFile); err != nil {
|
||||
return
|
||||
}
|
||||
if fh, err = os.Open(rcPath); err == nil {
|
||||
opt.expressions = append(opt.expressions, fh) // rc should be the first source to be read
|
||||
opt.rcCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func listBuiltins() {
|
||||
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
|
||||
if imported {
|
||||
name = "*" + name
|
||||
}
|
||||
fmt.Printf("%20q: %s\n", name, description)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (opt *Options) parseArgs() (err error) {
|
||||
for i := 1; i < len(os.Args) && err == nil; i++ {
|
||||
arg := os.Args[i]
|
||||
switch arg {
|
||||
case "-i":
|
||||
opt.forceInteractive = true
|
||||
case "-t":
|
||||
opt.printTree = true
|
||||
case "-p":
|
||||
opt.printPrefix = true
|
||||
case "-e":
|
||||
if i+1 < len(os.Args) {
|
||||
i++
|
||||
spec := os.Args[i]
|
||||
if strings.HasPrefix(spec, "@") {
|
||||
var f *os.File
|
||||
if f, err = os.Open(spec[1:]); err == nil {
|
||||
opt.expressions = append(opt.expressions, f)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if len(spec) > 0 && spec[len(spec)-1] != '\n' {
|
||||
spec += "\n"
|
||||
}
|
||||
opt.expressions = append(opt.expressions, strings.NewReader(spec))
|
||||
}
|
||||
} else {
|
||||
err = errOptValueRequired(arg)
|
||||
}
|
||||
case "-b":
|
||||
if i+1 < len(os.Args) {
|
||||
i++
|
||||
specs := strings.Split(os.Args[i], ",")
|
||||
if len(specs) == 1 {
|
||||
opt.builtin = append(opt.builtin, specs[0])
|
||||
} else {
|
||||
opt.builtin = append(opt.builtin, specs)
|
||||
}
|
||||
} else {
|
||||
err = errOptValueRequired(arg)
|
||||
}
|
||||
case "-B", "--list-builtins":
|
||||
listBuiltins()
|
||||
os.Exit(0)
|
||||
case "-m", "--modules":
|
||||
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
|
||||
fmt.Printf("%20q: %s\n", name, description)
|
||||
return true
|
||||
})
|
||||
os.Exit(0)
|
||||
case "--noout":
|
||||
opt.output = false
|
||||
case "-h", "--help", "help":
|
||||
cmdHandler.help()
|
||||
os.Exit(0)
|
||||
case "-v", "--version", "version", "about":
|
||||
fmt.Println(about())
|
||||
os.Exit(0)
|
||||
default:
|
||||
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
# All rights reserved.
|
||||
|
||||
RESOURCE_FILE=".build.rc"
|
||||
BUILD_REGISTER=".build_register"
|
||||
PASSWORD=
|
||||
GITEA_USER="camoroso"
|
||||
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||
GITEA_OWNER="go-pkg"
|
||||
GITEA_HOST="https://git.portale-stac.it"
|
||||
GITEA_BASE_PATH="api/packages"
|
||||
GITEA_PKG_TYPE="generic"
|
||||
|
||||
function exitMsg() {
|
||||
echo >&2 "${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function readBuildCount() {
|
||||
local reg ver count
|
||||
if [ -r "${BUILD_REGISTER}" ]; then
|
||||
reg=$(<"${BUILD_REGISTER}")
|
||||
else
|
||||
reg="${PROGRAM_VERSION} 0"
|
||||
fi
|
||||
read ver count <<<"${reg}"
|
||||
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||
count=0
|
||||
fi
|
||||
echo ${count}
|
||||
}
|
||||
|
||||
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
else
|
||||
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||
fi
|
||||
if [ -z "${PASSWORD}" ]; then
|
||||
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||
fi
|
||||
|
||||
if ! ./build.bash; then
|
||||
exitMsg "Build program failed"
|
||||
fi
|
||||
|
||||
if ! source "${RESOURCE_FILE}"; then
|
||||
exitMsg "Loading resource file failed"
|
||||
fi
|
||||
|
||||
if ! exeList=$(echo 2>/dev/null ${PROGRAM_NAME}_v${PROGRAM_VERSION}_*); then
|
||||
exitMsg "No executable found"
|
||||
fi
|
||||
|
||||
buildCount=$(readBuildCount)
|
||||
fileCount=0
|
||||
for exe in ${exeList}; do
|
||||
if [ "${exe/tar.gz/}" != "${exe}" ]; then
|
||||
continue
|
||||
fi
|
||||
((fileCount++))
|
||||
dir="${exe}_${buildCount}"
|
||||
dist="${dir}.tar.gz"
|
||||
rm -f "${dist}"
|
||||
printf "%2d: %-30s --> %s\n" "${fileCount}" "${exe}" "${dist}"
|
||||
mkdir "${dir}"
|
||||
cp "${exe}" "${dir}/${PROGRAM_NAME}"
|
||||
tar czf "${dist}" "${dir}"
|
||||
rm -fR "${dir}"
|
||||
|
||||
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${dist}"
|
||||
# echo "${url}"
|
||||
curl --user "${USER}:${PASSWORD}" --upload-file "${dist}" "${url}"
|
||||
|
||||
rm -f "${dist}"
|
||||
done
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// util-string.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func checkStringLiteral(literal string) (value string, err error) {
|
||||
length := len(literal)
|
||||
if length >= 2 {
|
||||
if (literal[0] == '"' && literal[length-1] == '"') || literal[0] == '\'' && literal[length-1] == '\'' {
|
||||
value = literal[1 : length-1]
|
||||
} else {
|
||||
err = fmt.Errorf("unquoted or partially quoted string literal: `%s`", literal)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("invalid string literal: `%s`", literal)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1.17.0
|
||||
+48
-46
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// data-cursors.go
|
||||
@@ -7,24 +7,26 @@ package expr
|
||||
import (
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type dataCursor struct {
|
||||
ds map[string]Functor
|
||||
ctx ExprContext
|
||||
ds map[string]kern.Functor
|
||||
ctx kern.ExprContext
|
||||
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
|
||||
// cursorValid bool // true if resource is nil or if clean has not yet been called
|
||||
index int
|
||||
count int
|
||||
index int64
|
||||
count int64
|
||||
current any
|
||||
lastErr error
|
||||
resource any
|
||||
nextFunc Functor
|
||||
cleanFunc Functor
|
||||
resetFunc Functor
|
||||
nextFunc kern.Functor
|
||||
cleanFunc kern.Functor
|
||||
resetFunc kern.Functor
|
||||
}
|
||||
|
||||
func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
|
||||
func NewDataCursor(ctx kern.ExprContext, ds map[string]kern.Functor, resource any) (dc *dataCursor) {
|
||||
dc = &dataCursor{
|
||||
ds: ds,
|
||||
initState: true,
|
||||
@@ -35,14 +37,14 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
|
||||
lastErr: nil,
|
||||
resource: resource,
|
||||
ctx: ctx.Clone(),
|
||||
nextFunc: ds[NextName],
|
||||
cleanFunc: ds[CleanName],
|
||||
resetFunc: ds[ResetName],
|
||||
nextFunc: ds[kern.NextName],
|
||||
cleanFunc: ds[kern.CleanName],
|
||||
resetFunc: ds[kern.ResetName],
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *dataCursor) Context() ExprContext {
|
||||
func (dc *dataCursor) Context() kern.ExprContext {
|
||||
return dc.ctx
|
||||
}
|
||||
|
||||
@@ -79,27 +81,27 @@ func (dc *dataCursor) String() string {
|
||||
}
|
||||
|
||||
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
||||
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
|
||||
exists = slices.Contains([]string{kern.CleanName, kern.ResetName, kern.CurrentName, kern.IndexName}, name)
|
||||
if !exists {
|
||||
f, ok := dc.ds[name]
|
||||
exists = ok && isFunctor(f)
|
||||
exists = ok && kern.IsFunctor(f)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
|
||||
if name == IndexName {
|
||||
if name == kern.IndexName {
|
||||
value = int64(dc.Index())
|
||||
} else if name == CleanName {
|
||||
} else if name == kern.CleanName {
|
||||
err = dc.Clean()
|
||||
} else if name == ResetName {
|
||||
} else if name == kern.ResetName {
|
||||
err = dc.Reset()
|
||||
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
} else if functor, ok := dc.ds[name]; ok && kern.IsFunctor(functor) {
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
value, err = functor.InvokeNamed(ctx, name, args)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
} else {
|
||||
err = errNoOperation(name)
|
||||
err = kern.ErrNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -127,10 +129,10 @@ func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any
|
||||
|
||||
func (dc *dataCursor) Reset() (err error) {
|
||||
if dc.resetFunc != nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
||||
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(dc.resetFunc, []any{dc.resource})
|
||||
_, err = dc.resetFunc.InvokeNamed(ctx, kern.ResetName, actualParams)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
}
|
||||
dc.index = -1
|
||||
dc.count = 0
|
||||
@@ -142,10 +144,10 @@ func (dc *dataCursor) Reset() (err error) {
|
||||
|
||||
func (dc *dataCursor) Clean() (err error) {
|
||||
if dc.cleanFunc != nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
||||
exportObjects(dc.ctx, ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||
_, err = dc.cleanFunc.InvokeNamed(ctx, kern.CleanName, actualParams)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
}
|
||||
dc.lastErr = io.EOF
|
||||
return
|
||||
@@ -178,13 +180,13 @@ func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
|
||||
func (dc *dataCursor) checkFilter(filter kern.Functor, item any) (accepted bool, err error) {
|
||||
var v any
|
||||
var ok bool
|
||||
ctx := cloneContext(dc.ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
|
||||
actualParams := bindActualParams(filter, []any{item, dc.index})
|
||||
if v, err = filter.InvokeNamed(ctx, FilterName, actualParams); err == nil && v != nil {
|
||||
actualParams := kern.BindActualParams(filter, []any{item, dc.index})
|
||||
if v, err = filter.InvokeNamed(ctx, kern.FilterName, actualParams); err == nil && v != nil {
|
||||
if accepted, ok = v.(bool); !ok {
|
||||
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
|
||||
}
|
||||
@@ -192,10 +194,10 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
|
||||
return
|
||||
}
|
||||
|
||||
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
actualParams := bindActualParams(mapper, []any{item, dc.index})
|
||||
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
|
||||
func (dc *dataCursor) mapItem(mapper kern.Functor, item any) (mappedItem any, err error) {
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
actualParams := kern.BindActualParams(mapper, []any{item, dc.index})
|
||||
mappedItem, err = mapper.InvokeNamed(ctx, kern.MapName, actualParams)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -213,15 +215,15 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
||||
return
|
||||
}
|
||||
current = dc.current
|
||||
filter := dc.ds[FilterName]
|
||||
mapper := dc.ds[MapName]
|
||||
filter := dc.ds[kern.FilterName]
|
||||
mapper := dc.ds[kern.MapName]
|
||||
var item any
|
||||
for item == nil && dc.lastErr == nil {
|
||||
ctx := cloneContext(dc.ctx)
|
||||
ctx := kern.CloneContext(dc.ctx)
|
||||
dc.index++
|
||||
|
||||
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
|
||||
actualParams := kern.BindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, kern.NextName, actualParams); dc.lastErr == nil {
|
||||
if item == nil {
|
||||
dc.lastErr = io.EOF
|
||||
} else {
|
||||
@@ -239,7 +241,7 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
||||
}
|
||||
}
|
||||
}
|
||||
exportObjects(dc.ctx, ctx)
|
||||
kern.ExportObjects(dc.ctx, ctx)
|
||||
}
|
||||
dc.current = item
|
||||
if dc.lastErr != nil {
|
||||
@@ -296,10 +298,10 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
||||
// return
|
||||
// }
|
||||
|
||||
func (dc *dataCursor) Index() int {
|
||||
func (dc *dataCursor) Index() int64 {
|
||||
return dc.index - 1
|
||||
}
|
||||
|
||||
func (dc *dataCursor) Count() int {
|
||||
func (dc *dataCursor) Count() int64 {
|
||||
return dc.count
|
||||
}
|
||||
|
||||
+33
-29
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
+333
-57
@@ -1,6 +1,7 @@
|
||||
= Expr
|
||||
Expressions calculator
|
||||
:authors: Celestino Amoroso
|
||||
:email: celestino.amoroso@gmail.com
|
||||
:docinfo: shared
|
||||
:encoding: utf-8
|
||||
:toc: right
|
||||
@@ -16,10 +17,11 @@ Expressions calculator
|
||||
:sectlinks:
|
||||
:sectanchors:
|
||||
:source-highlighter: rouge
|
||||
// :rouge-style: ThankfulEyes
|
||||
:rouge-style: gruvbox
|
||||
// :rouge-style: colorful
|
||||
//:rouge-style: monokay
|
||||
// :rouge-style: gruvbox
|
||||
:rouge-style: manni
|
||||
:stylesdir: /home/share/s3-howto/styles
|
||||
:stylesheet: adoc-colony.css
|
||||
|
||||
// Workaround to manage double-column in back-tick quotes
|
||||
:2c: ::
|
||||
// Workaround to manage double-plus in back-tick quotes
|
||||
@@ -34,7 +36,7 @@ Expressions calculator
|
||||
|
||||
toc::[]
|
||||
|
||||
#TODO: Work in progress (last update on 2026/04/21, 6:49 p.m.)#
|
||||
#TODO: Work in progress#
|
||||
|
||||
== Expr
|
||||
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
||||
@@ -74,7 +76,7 @@ Imported functions are registered in the _global context_. When an expression fi
|
||||
===== Inspecting contexts
|
||||
_Expr_ provides the operator [blue]_$$_ that returns the current context. This can be used to inspect the content of the context, for example to check the value of a variable or to see which functions are currently linked to the context. This operator is primarily intended for debugging purposes.
|
||||
|
||||
An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be used to inspect contexts interactively.
|
||||
An interactive tool could like `ecli` (see <<_ecli_test_tool>>) can be used to inspect contexts interactively.
|
||||
|
||||
|
||||
.Example: inspecting contexts
|
||||
@@ -100,26 +102,48 @@ An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be u
|
||||
[green]`{2sp}}` +
|
||||
[green]`}`
|
||||
|
||||
In order to inspect the global context issue the [blue]`$$global` operator.
|
||||
In order to inspect the global context issue the [blue]`$$ global` operation.
|
||||
////
|
||||
.Example: list all functions whose name starts with "str"
|
||||
`>>>` [blue]`builtin "string` [gray]__// most function in the builtin module *string* have names starting with "str".__ +
|
||||
[green]`1`
|
||||
|
||||
=== `dev-expr` test tool
|
||||
Before we begin to describe the syntax of _Expr_, it is worth introducing _dev-expr_ because it will be used to show many examples of expressions.
|
||||
:dollar: $
|
||||
:2dollars: $$
|
||||
|
||||
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provided an important aid for quickly testing of new features during their development.
|
||||
`>>>` [blue]`($(($$global).functions) filter strStartsWith($_, "str"))` +
|
||||
[green]`[` +
|
||||
[green]`{2sp}"strEndsWith",` +
|
||||
[green]`{2sp}"strJoin",` +
|
||||
[green]`{2sp}"strLower",` +
|
||||
[green]`{2sp}"strSplit",` +
|
||||
[green]`{2sp}"strStartsWith",` +
|
||||
[green]`{2sp}"strSub",` +
|
||||
[green]`{2sp}"strTrim",` +
|
||||
[green]`{2sp}"strUpper",` +
|
||||
[green]`{2sp}"string"` +
|
||||
[green]`]`
|
||||
////
|
||||
|
||||
`dev-expr` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
||||
[[sec_ecli]]
|
||||
=== `ecli` Expression Calculator Interactive Tool
|
||||
Before we begin to describe the syntax of _Expr_, it is worth introducing _ecli_, former _dev-expr_, because it will be used to show many examples of expressions.
|
||||
|
||||
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/[dev-expr].
|
||||
`ecli` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `ecli` provided an important aid for quickly testing of new features during their development.
|
||||
|
||||
`ecli` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
||||
|
||||
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/ecli/[ecli].
|
||||
|
||||
Here are some examples of execution.
|
||||
|
||||
.Run `dev-expr` in REPL mode and ask for help
|
||||
.Run `ecli` in REPL mode and ask for help
|
||||
[source,shell]
|
||||
----
|
||||
# Type 'exit' or Ctrl+D to quit the program.
|
||||
|
||||
[user]$ ./dev-expr
|
||||
dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
||||
[user]$ ./ecli
|
||||
ecli -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.26.0
|
||||
Type help to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
@@ -155,8 +179,8 @@ dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoros
|
||||
.REPL examples
|
||||
[source,shell]
|
||||
----
|
||||
[user]$ ./dev-expr
|
||||
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
||||
[user]$ ./ecli
|
||||
ecli -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.19.0
|
||||
Type help to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
@@ -223,14 +247,20 @@ Value range: *-9223372036854775808* to *9223372036854775807*
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
|
||||
| [blue]`+` | _Sum_ | Add two values^(<<note_int_plus_string,1>>)^ | [blue]`-1 + 2` -> _1_
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
|
||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
|
||||
| [blue]`*` | _Product_ | Multiply two values^(<<note_string_repl,2>>)^ | [blue]`-1 * 2` -> _-2_
|
||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(<<note_float_division,3>>)^ | [blue]`-11 / 2` -> _-5_
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
||||
|===
|
||||
[[note_int_plus_string]]
|
||||
^(1)^ The sum operator [blue]`+` also supports adding an integer number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 48` results in `"x48"`.
|
||||
|
||||
^(*)^ See also the _float division_ [blue]`./` below.
|
||||
[[note_string_repl]]
|
||||
^(2)^ The product operator also supports multiplying a string by an integer. In this case, the number represents homw may times the string has to be repeated in the result, e.g. `"foo" * 3` returnsn `"foofoofoo"`.
|
||||
|
||||
[[note_float_division]]
|
||||
^(3)^ See also the _float division_ [blue]`./` below.
|
||||
|
||||
|
||||
==== Floats
|
||||
@@ -268,12 +298,14 @@ _dec-seq_ = _see-integer-literal-syntax_
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`+` | _Sum_ | Add two values^(<<note_float_plus_string,1>>)^ | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
||||
| [blue]`/` | _Float division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||
|===
|
||||
[[note_float_plus_string]]
|
||||
^(1)^ The sum operator [blue]`+` also supports adding a float number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 1.2` results in `"x1.2"`.
|
||||
|
||||
==== Fractions
|
||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||
@@ -447,11 +479,11 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
|
||||
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and, as a conseguence, its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
||||
|
||||
|
||||
TIP: `dev-expr` provides the _ctrl()_ function that allows to change this behaviour.
|
||||
TIP: `ecli` provides the _ctrl()_ function that allows to change this behaviour.
|
||||
====
|
||||
|
||||
=== Lists
|
||||
_Expr_ supports list of mixed-type values, also specified by normal expressions. Internally, _Expr_'s lists are Go arrays.
|
||||
_Expr_ supports list of mixed-type values, also specified by normal expressions. Internally, _Expr_'s lists are Go slices.
|
||||
|
||||
.List literal syntax
|
||||
====
|
||||
@@ -556,7 +588,21 @@ Array's items can be accessed using the index `[]` operator.
|
||||
`>>>` [blue]`[1,2,3,2,4] - [2,4]` +
|
||||
[green]`[1, 3]`
|
||||
|
||||
=== Linked Lists
|
||||
Linked lists are a special kind of lists that are used to represent sequences of items that can be easily modified. They are represented as lists of two items: the first item is the value of the current node, and the second item is the next node in the list. The last node in the list has a next node that is _nil_.
|
||||
|
||||
Internally,Expr's linked lists hold two pointers: one to the head of the list and one to the tail of the list. This allows for efficient insertion and deletion of items at both ends of the list. Also, linked lists keep track of their size, so the number of items in a linked list can be obtained in constant time.
|
||||
|
||||
.Linked List literal syntax
|
||||
====
|
||||
*_linked-list_* = "**[<**" [_item-expr_ {"**,**" _item-expr_}] "**>]**" +
|
||||
_item-expr_ = _any-value_
|
||||
====
|
||||
|
||||
`>>>` [blue]`ls=[<1,2,3,4>]` +
|
||||
[green]`[<1, 2, 3, 4>]`
|
||||
|
||||
#todo: to be completed#
|
||||
|
||||
=== Dictionaries
|
||||
The _dictionary_, or _dict_, data-type represents sets of pairs _key/value_. It is also known as _map_ or _associative array_.
|
||||
@@ -698,9 +744,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 +783,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 +901,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]`cat` | _Infix_ | _Item-concatenation_ | _iterable_ `cat ` _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 +992,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 +1081,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 +1119,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 +1157,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 +1171,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 +1251,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 +1263,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 +1368,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 +1391,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 +1417,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 +1438,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 +1448,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 +1500,129 @@ 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()
|
||||
Syntax: +
|
||||
`{4sp}fileByteIterator(handle-or-path) -> iterator`
|
||||
|
||||
Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||
|
||||
.Examples
|
||||
>>> builtin "os.file" +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`fileByteIterator("test-file.txt") map $_` +
|
||||
[green]`[
|
||||
117,
|
||||
110,
|
||||
111,
|
||||
10,
|
||||
100,
|
||||
117,
|
||||
101,
|
||||
10
|
||||
]`
|
||||
|
||||
>>> builtin ["os.file", "string"] +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`fileByteIterator("test-file.txt") map char($_)` +
|
||||
[green]`[
|
||||
"u",
|
||||
"n",
|
||||
"o",
|
||||
"
|
||||
",
|
||||
"d",
|
||||
"u",
|
||||
"e",
|
||||
"
|
||||
",
|
||||
]`
|
||||
|
||||
===== fileLineIterator()
|
||||
Syntax: +
|
||||
`{4sp}fileLineIterator(handle-or-path) -> iterator`
|
||||
|
||||
Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`builtin "os.file"` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`fileLineIterator("test-file.txt") map $_` +
|
||||
[green]`[
|
||||
"uno",
|
||||
"due"
|
||||
]`
|
||||
|
||||
|
||||
==== 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: +
|
||||
@@ -1541,6 +1726,8 @@ Returns a string obtained by converting all characters of the specified string t
|
||||
`>>>` [blue]`strLower("Hello, world!")` +
|
||||
[green]`"hello, world!"`
|
||||
|
||||
|
||||
|
||||
== Iterators
|
||||
Iterators are objects that can be used to traverse collections, such as lists and dictionaries. They are created by providing a _data-source_ object, the collection, in a `$(<data-source>)` expression. Once an iterator is created, it can be used to access the elements of the collection one by one.
|
||||
|
||||
@@ -1555,16 +1742,16 @@ _data-source_ = _explicit_ | _list-spec_ | _dict-spec_ | _custom-data-source_
|
||||
|
||||
_explicit_ = _any-expr_ { "**,**" _any-expr_ }
|
||||
|
||||
_list-spec_ = _list_ _range-options_ +
|
||||
_list-spec_ = _list_ ["**,**" _range-options_] +
|
||||
_list_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" +
|
||||
_range-options_ = [ "**,**" _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]]] +
|
||||
_range-options_ = _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]] +
|
||||
_start-index_, _end-index_, _step_ = _integer-expr_
|
||||
|
||||
_dict-spec_ = _dict_ _dict-options_ +
|
||||
_dict-spec_ = _dict_ ["**,**" _dict-options_] +
|
||||
_dict_ = "**{**" _key-value-pair_ { "**,**" _key-value-pair_ } "**}**" +
|
||||
_key-value-pair_ = _scalar-value_ "**:**" _any-expr_ +
|
||||
_scalar-value_ = _string_ | _number_ | _boolean_ +
|
||||
_dict-options_ = [ "**,**" _sort-order_ [ "**,**" _iter-mode_ ] ] +
|
||||
_dict-options_ = _sort-order_ [ "**,**" _iter-mode_ ] +
|
||||
_sort-order_ = _asc-order_ | _desc-order_ | _no-sort_ | _default-order_ +
|
||||
_asc-order_ = "**asc**" | "**a**" +
|
||||
_desc-order_ = "**desc**" | "**d**" +
|
||||
@@ -1642,7 +1829,7 @@ Negative steps are also allowed. They are interpreted as reverse iteration. For
|
||||
The above example shows the use of the [blue]`{plusplus}` operator to get the next element of an iterator. The [blue]`{plusplus}` operator is a postfix operator that can be used with iterators. It returns the next element of the collection and updates the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_.
|
||||
|
||||
|
||||
After the first use of the [blue]`{plusplus}` operator, the prefixed operato [blue]`{star}` operator can be used to get the current element of the collection without updating the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_. Same error is returned if the [blue]`{star}` operator is used before the first use of the [blue]`{plusplus}` operator.
|
||||
After the first use of the [blue]`{plusplus}` operator, the prefixed operator [blue]`{star}` operator can be used to get the current element of the collection without updating the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_. Same error is returned if the [blue]`{star}` operator is used before the first use of the [blue]`{plusplus}` operator.
|
||||
|
||||
.Example: using the [blue]`{star}` operator
|
||||
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
||||
@@ -1654,6 +1841,11 @@ After the first use of the [blue]`{plusplus}` operator, the prefixed operato [bl
|
||||
`>>>` [blue]`{star}it` +
|
||||
[green]`"one"`
|
||||
|
||||
==== [blue]*`$$()`* -- Expansion special function for iterators
|
||||
The [blue]`$$()` operator is a special function already seen applied to contexts. It can also be applied that can be used with iterators. When applied to an iterator, it returns a _linked list_ of all the remaining elements of the collection. The state of the iterator is updated to the end of the collection. If there are no more elements to iterate over, it returns an empty list.
|
||||
|
||||
#todo: examples#
|
||||
|
||||
==== Named operators
|
||||
Named operators are operators that are identified by a name instead of a symbol. They are implicitly defined and can be called using a special syntax. For example, the [blue]`{plusplus}` has the equivalent named operator [blue]`.next`.
|
||||
|
||||
@@ -1682,6 +1874,90 @@ TIP: Iterators built on custom data-sources can provide additional named operato
|
||||
`>>>` [blue]`it.next` +
|
||||
[green]`"one"`
|
||||
|
||||
=== Infixed operators on iterators
|
||||
There are also some infixed operators that can be used with iterators. They are defined as follows.
|
||||
|
||||
* <<_cat,cat operator>>
|
||||
* <<_digest,digest operator>>
|
||||
* <<_filter,filter operator>>
|
||||
* <<_groupby,groupby operator>>
|
||||
* <<_map,map operator>>
|
||||
//* <<_reduce,reduce operator>>: applies a binary expression cumulatively to the elements of the iterator, from left to right, to reduce the iterator to a single value.
|
||||
//* <<_zip,zip operator>>: takes two or more iterators and returns a list of tuples, where the i-th tuple contains the i-th element from each of the input iterators.
|
||||
|
||||
|
||||
==== Automatic variables in operators
|
||||
At each iteration, the following automatic variables are available for use in the expression of the [blue]`digest`, [blue]`filter`, [blue]`groupby`, and [blue]`map` operators.
|
||||
|
||||
* `$_`: the current element of the iterator.
|
||||
* `$__`: the index of the current element in the iterator, starting from 0.
|
||||
* `$#`: the number of elements already visited in the iterator, starting from 0.
|
||||
|
||||
---
|
||||
|
||||
==== [blue]`cat` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> cat <iterable> -> <iterator>{4sp}`
|
||||
|
||||
[blue]`cat` operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.
|
||||
|
||||
.Examples
|
||||
#todo: examples#
|
||||
|
||||
==== [blue]`filter` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> filter <expr> -> <iterator>{4sp}`
|
||||
|
||||
[blue]`filter` applies a boolean expression to each element of the iterator and returns a new iterator that only produces the elements of the first iterator for which the expression evaluates to true.
|
||||
|
||||
.Examples
|
||||
#todo: examples#
|
||||
|
||||
|
||||
==== [blue]`groupby` operator
|
||||
Syntax: +
|
||||
`{4sp}<list-of-dicts> groupby <key> -> <dict>{4sp}`
|
||||
|
||||
The left side of [blue]`groupby` operator is a list ofdictionaries or an iterator over a list of dictionaries. It takes a key and returns a dictionary where the keys are the unique values of the specified key in the dictionaries and the values are lists of dictionaries that have that key value. In other words, it groups the dictionaries by the specified key value.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}, {"name": "Charlie", "age": 30}] groupby "age"` +
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
25: [
|
||||
{
|
||||
"name": "Bob",
|
||||
"age": 25
|
||||
}
|
||||
],
|
||||
30: [
|
||||
{
|
||||
"name": "Alice",
|
||||
"age": 30
|
||||
},
|
||||
{
|
||||
"name": "Charlie",
|
||||
"age": 30
|
||||
}
|
||||
]
|
||||
}
|
||||
----
|
||||
|
||||
==== [blue]`map` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> map <expr> -> <iterator>{4sp}`
|
||||
|
||||
[blue]`map` operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a new iterator over the computed values of the that expression. The current element of the iterator is available in the expression as the variable `$_`.
|
||||
|
||||
.Example: using the [blue]`map` operator
|
||||
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
||||
[green]`$(#3)` +
|
||||
`>>>` [blue]`excl_it = it map $_ + "!"` +
|
||||
[green]`$($([#3]))` +
|
||||
`>>>` [blue]`$$(excl_it)` +
|
||||
[green]`[<"one!", "two!", "three!">]`
|
||||
|
||||
=== Iterator over custom data-sources
|
||||
It is possible to create iterators over custom data-sources by defining a dictionary that has the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.
|
||||
|
||||
|
||||
+460
-30
@@ -581,7 +581,11 @@ pre.rouge .ss {
|
||||
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
|
||||
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
||||
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li>
|
||||
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></li>
|
||||
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -613,8 +617,10 @@ pre.rouge .ss {
|
||||
<li><a href="#_dec">dec()</a></li>
|
||||
<li><a href="#_string">string()</a></li>
|
||||
<li><a href="#_fract">fract()</a></li>
|
||||
<li><a href="#_char">char()</a></li>
|
||||
<li><a href="#_eval">eval()</a></li>
|
||||
<li><a href="#_var">var()</a></li>
|
||||
<li><a href="#_set">set</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
||||
@@ -649,6 +655,8 @@ pre.rouge .ss {
|
||||
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
||||
<li><a href="#_filereadtext">fileReadText()</a></li>
|
||||
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
||||
<li><a href="#_filebyteiterator">fileByteIterator()</a></li>
|
||||
<li><a href="#_filelineiterator">fileLineIterator()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
||||
@@ -674,7 +682,15 @@ pre.rouge .ss {
|
||||
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></li>
|
||||
<li><a href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></li>
|
||||
<li><a href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></li>
|
||||
<li><a href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></li>
|
||||
<li><a href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_plugins">9. Plugins</a></li>
|
||||
@@ -686,7 +702,7 @@ pre.rouge .ss {
|
||||
<div class="sectionbody">
|
||||
<!-- toc disabled -->
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)</mark></p>
|
||||
<p><mark>TODO: Work in progress (last update on 2026/05/08)</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -788,7 +804,26 @@ pre.rouge .ss {
|
||||
<code class="green">}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In order to inspect the global context issue the <code class="blue">$$global</code> operator.</p>
|
||||
<p>In order to inspect the global context issue the <code class="blue">$$ global</code> operation.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example: list all functions whose name starts with "str"</div>
|
||||
<p><code>>>></code> <code class="blue">builtin "string</code> <em class="gray">// most function in the builtin module <strong>string</strong> have names starting with "str".</em><br>
|
||||
<code class="green">1</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">($$$global).functions) filter strStartsWith($_, "str"</code><br>
|
||||
<code class="green">[</code><br>
|
||||
<code class="green">  "strEndsWith",</code><br>
|
||||
<code class="green">  "strJoin",</code><br>
|
||||
<code class="green">  "strLower",</code><br>
|
||||
<code class="green">  "strSplit",</code><br>
|
||||
<code class="green">  "strStartsWith",</code><br>
|
||||
<code class="green">  "strSub",</code><br>
|
||||
<code class="green">  "strTrim",</code><br>
|
||||
<code class="green">  "strUpper",</code><br>
|
||||
<code class="green">  "string"</code><br>
|
||||
<code class="green">]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1880,10 +1915,10 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 13. Selector literal Syntax</div>
|
||||
<div class="title">Example 13. Selector literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||
<p><strong><em>selector-operator</em></strong> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
|
||||
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
||||
<em>item</em> = <em>expression</em><br>
|
||||
@@ -1931,11 +1966,45 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
||||
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_triple_special_case_of_the_selector_operator"><a class="anchor" href="#_triple_special_case_of_the_selector_operator"></a><a class="link" href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>If the <em>select-expression</em> is a boolean expression, the selector operator can be used as a sort of <em>if-then-else</em> statement. In this case, the first case is evaluated if the <em>select-expression</em> is true, and the second case is evaluated if the <em>select-expression</em> is false. In this special case, the <em>match-list</em> of both cases must be empty.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">(true) ? {"T"}: {"F"}</code><br>
|
||||
<code class="green">T</code><br>
|
||||
<code>>>></code> <code class="blue">(2 > 1) ? {"a"} : {"b"}</code><br>
|
||||
<code class="green">a</code><br>
|
||||
<code>>>></code> <code class="blue">(2 < 1) ? {"a"} : {"b"}</code><br>
|
||||
<code class="green">b</code></p>
|
||||
</div>
|
||||
<div class="admonitionblock warning">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-warning" title="Warning"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
<div class="paragraph">
|
||||
<p>The triple special case of the selector operator is very useful, but it only works with boolean expressions.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example of confusion</div>
|
||||
<p><code>>>></code> <code class="blue">int(true) ? {"T"}: {"F"}</code><br>
|
||||
<code class="green">F</code></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, 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.</p>
|
||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, 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.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
@@ -1953,7 +2022,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the variable on the left side.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The third one, <code class="blue">?!</code>, is the alternate operator. If the variable on the left size is not defined, it returns <em class="blue">nil</em>. Otherwise it returns the result of the expressione on the right side.</p>
|
||||
@@ -1965,7 +2034,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<i class="fa icon-important" title="Important"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
If the left variable is NOT defined, the right expression is not evaluated at all.
|
||||
If the variable <code class="blue">?!</code> is NOT defined, the expression is not evaluated at all.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -2371,7 +2440,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special allocation operators below</em></p></td>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special assignment operators below</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
||||
@@ -2381,6 +2450,40 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>ITER-OP</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">digest</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-digesting</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>digest</code> <em>expr</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">filter</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-filtering</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>filter</code> <em>expr</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">groupby</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict-grouping</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>groupby</code> <em>key-expr</em> → <em>dict</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">cat</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-concatenation</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> `cat ` <em>iterable</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">map</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-mapping</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>map</code> <em>-expr</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See iterators section for examples</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
@@ -2536,14 +2639,14 @@ short for<br>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}</code><br>
|
||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}</code><br>
|
||||
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another document that I’ll write, one day, maybe.</p>
|
||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another documents that I’ll write, one day, maybe.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -2642,7 +2745,7 @@ short for<br>
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">f = func() { @x = 3; x = 5 }</code> <em class="gray">// f() declares two <strong>different</strong> local variables: <code>@x</code> and <code>x</code></em><br>
|
||||
<code class="green">f():any{}</code><br>
|
||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two) in f() is calculated and the last result is returned</em><br>
|
||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two expressions) in f() is calculated and the last result is returned</em><br>
|
||||
<code class="green">5</code><br>
|
||||
<code>>>></code> <code class="blue">x</code> <em class="gray">// The <code>x</code> variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the <code>@x</code> variable, local to f() after its termnation.</em><br>
|
||||
<code class="green">3</code></p>
|
||||
@@ -2727,7 +2830,7 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
||||
<div class="title">Example 16. Builtin activation syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em>)<br>
|
||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em> | <strong>"*"</strong>)<br>
|
||||
<em>builtin-name</em> = <em>string</em><br>
|
||||
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
||||
</div>
|
||||
@@ -2815,9 +2918,15 @@ To avoid the need to activate builtin modules one by one, it is possible to acti
|
||||
<div class="title">Other functions</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_char">char()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_eval">eval()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_set">set()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_var">var()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -2833,7 +2942,9 @@ Returns <em>true</em> if the value type of <em><expr></em> is boolean, fal
|
||||
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
||||
<code class="green">true</code></p>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isBool(3 + 2)</code><br>
|
||||
<code class="green">false</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2938,7 +3049,7 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction or
|
||||
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
||||
Returns a boolean value , false otherwise.</p>
|
||||
Returns <em>true</em> if the value type of <em><expr></em> is string, false otherwise.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
@@ -2954,7 +3065,7 @@ Returns a boolean value , false otherwise.</p>
|
||||
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
||||
Returns a <em>boolean</em> value consisent to the value of the expression.</p>
|
||||
Returns a <em>boolean</em> value consisent with the value of the expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
@@ -3077,6 +3188,20 @@ Returns a <em>fraction</em> value consistent with the value of the expression.</
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_char"><a class="anchor" href="#_char"></a><a class="link" href="#_char">char()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>char(<intexpr>) → string</code><br>
|
||||
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">char(65)</code><br>
|
||||
<code class="green">"A"</code><br>
|
||||
<code>>>></code> <code class="blue">char(97)</code><br>
|
||||
<code class="green">"a"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
||||
@@ -3096,13 +3221,13 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
<code>    var(<string-expr>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||
<code>>>></code> <code class="blue">var("$"+"x")</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
||||
<code class="green">12</code><br>
|
||||
@@ -3110,9 +3235,32 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
<code class="green">13</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_set"><a class="anchor" href="#_set"></a><a class="link" href="#_set">set</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    set(<string-expr>, <expr>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">set("$x", 100)</code><br>
|
||||
<code class="green">100</code><br>
|
||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||
<code class="green">100</code><br></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
||||
|
||||
@@ -3124,10 +3272,18 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "import"</code></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
||||
<div class="paragraph">
|
||||
<p><em class="blue">import(<span class="grey"><source-file></span>)</em> — loads the multi-expression contained in the specified source and returns its value.</p>
|
||||
<p>Syntax:<br>
|
||||
<code>    import(<source-file>)</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Loads the multi-expression contained in the specified source and returns its value.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3137,16 +3293,40 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "iterator"</code></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    run(<iterator>, <operator>, <vars>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Iterates over the specified iterator and applies the specified operator to the current value of the iterator.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "math.arith"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>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.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_add">add()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_mul">mul()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
||||
<div class="paragraph">
|
||||
@@ -3202,37 +3382,213 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "os.file"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The "os.file" module provides functions for working with files.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<div class="title">File related functions</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_fileOpen">fileOpen()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileAppend">fileAppend()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileCreate">fileCreate()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileClose">fileClose()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileWriteText">fileWriteText()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileReadText">fileReadText()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileReadTextAll">fileReadTextAll()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<div class="title">Iterator functions for files</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_fileByteIterator">fileByteIterator()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileLineIterator">fileLineIterator()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>More functions will be added in the future.</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileOpen(<file-path>) → file-handle</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>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.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileAppend(<file-path>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Like <a href="#_fileCreate">fileCreate()</a> but write operations happen at the end of the file.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileCreate(<file-path>) → file-handle</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Creates or truncates the named <em><file-path></em>. 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.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filebyteiterator"><a class="anchor" href="#_filebyteiterator"></a><a class="link" href="#_filebyteiterator">fileByteIterator()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileByteIterator(handle-or-path) → iterator</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> builtin "os.file"<br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map $_</code><br>
|
||||
<code class="green">[
|
||||
117,
|
||||
110,
|
||||
111,
|
||||
10,
|
||||
100,
|
||||
117,
|
||||
101,
|
||||
10
|
||||
]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>>>> builtin ["os.file", "string"]<br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map char($_)</code><br>
|
||||
<code class="green">[
|
||||
"u",
|
||||
"n",
|
||||
"o",
|
||||
"
|
||||
",
|
||||
"d",
|
||||
"u",
|
||||
"e",
|
||||
"
|
||||
",
|
||||
]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filelineiterator"><a class="anchor" href="#_filelineiterator"></a><a class="link" href="#_filelineiterator">fileLineIterator()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileLineIterator(handle-or-path) → iterator</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">builtin "os.file"</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">fileLineIterator("test-file.txt") map $_</code><br>
|
||||
<code class="green">[
|
||||
"uno",
|
||||
"due"
|
||||
]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "string"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This module provides functions for working with strings.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Currently available functions:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_strJoin">strJoin()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strSub">strSub()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strSplit">strSplit()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strTrim">strTrim()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strStartsWith">strStartsWith()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strEndsWith">strEndsWith()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strUpper">strUpper()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strLower">strLower()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect4">
|
||||
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
||||
<div class="paragraph">
|
||||
@@ -3589,7 +3945,81 @@ Iterators built on custom data-sources can provide additional named operators, d
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></h3>
|
||||
<h3 id="_infixed_operators_on_iterators"><a class="anchor" href="#_infixed_operators_on_iterators"></a><a class="link" href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>There are also some infixed operators that can be used with iterators. They are defined as follows.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_cat">cat operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_diget">digest operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_filter">filter operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_groupby">groupby operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_map">map operator</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect3">
|
||||
<h4 id="_cat_operator"><a class="anchor" href="#_cat_operator"></a><a class="link" href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> cat <iterable> → <iterator>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">cat</code> operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_filter_operator"><a class="anchor" href="#_filter_operator"></a><a class="link" href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> filter <expr> → <iterator>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">filter</code> applies a boolean expression to each element of the iterator and returns a list of the elements for which the expression evaluates to true.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_groupby_operator"><a class="anchor" href="#_groupby_operator"></a><a class="link" href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <dict> groupby <key> → <dict>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">groupby</code> operator groups the elements of the iterator based on the value of a specified expression and returns a dictionary where the keys are the group values and the values are lists of the elements in each group.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_map_operator"><a class="anchor" href="#_map_operator"></a><a class="link" href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> map <expr> → <list>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">map</code> operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable <code>$_</code>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example: using the <code class="blue">map</code> operator</div>
|
||||
<p><code>>>></code> <code class="blue">it = $(["one", "two", "three"])</code><br>
|
||||
<code class="green">$(#3)</code><br>
|
||||
<code>>>></code> <code class="blue">it map $_ + "!"</code><br>
|
||||
<code class="green">["one!", "two!", "three!"]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
||||
</div>
|
||||
@@ -3610,7 +4040,7 @@ Iterators built on custom data-sources can provide additional named operators, d
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2026-04-21 06:35:14 +0200
|
||||
Last updated 2026-05-12 16:25:27 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// expr-context.go
|
||||
package expr
|
||||
|
||||
// ----Expression Context
|
||||
type ExprContext interface {
|
||||
Clone() ExprContext
|
||||
SetParent(ctx ExprContext)
|
||||
GetParent() (ctx ExprContext)
|
||||
GetVar(varName string) (value any, exists bool)
|
||||
GetLast() any
|
||||
SetVar(varName string, value any)
|
||||
UnsafeSetVar(varName string, value any)
|
||||
|
||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||
VarCount() int
|
||||
DeleteVar(varName string)
|
||||
|
||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||
FuncCount() int
|
||||
DeleteFunc(funcName string)
|
||||
|
||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||
Call(name string, args map[string]any) (result any, err error)
|
||||
RegisterFuncInfo(info ExprFunc)
|
||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||
}
|
||||
@@ -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 &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() && 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 &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() && 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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
+68
-100
@@ -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)
|
||||
}
|
||||
}
|
||||
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) {
|
||||
func fixCtrlVar(name string) string {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
return name
|
||||
}
|
||||
|
||||
globalCtx.SetVar(name, newValue)
|
||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
name = fixCtrlVar(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 {
|
||||
name = fixCtrlVar(name)
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
}
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
return currentValue
|
||||
}
|
||||
|
||||
func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
if v, exists := ctx.GetVar(name); exists && IsBool(v) {
|
||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
name = fixCtrlVar(name)
|
||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||
currentStatus, _ = v.(bool)
|
||||
}
|
||||
|
||||
@@ -109,11 +76,9 @@ func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
return currentStatus
|
||||
}
|
||||
|
||||
func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
if v, exists := ctx.GetVar(name); exists && IsBool(v) {
|
||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
name = fixCtrlVar(name)
|
||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||
currentStatus, _ = v.(bool)
|
||||
}
|
||||
|
||||
@@ -121,36 +86,39 @@ func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
|
||||
return currentStatus
|
||||
}
|
||||
|
||||
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
|
||||
var v any
|
||||
var exists bool
|
||||
// func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
|
||||
// var v any
|
||||
// var exists bool
|
||||
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
// if !strings.HasPrefix(name, "_") {
|
||||
// name = "_" + name
|
||||
// }
|
||||
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
v, exists = globalCtx.GetVar(name)
|
||||
}
|
||||
// if v, exists = ctx.GetVar(name); !exists {
|
||||
// v, exists = globalCtx.GetVar(name)
|
||||
// }
|
||||
|
||||
if exists {
|
||||
if b, ok := v.(bool); ok {
|
||||
status = b
|
||||
// if exists {
|
||||
// if b, ok := v.(bool); ok {
|
||||
// status = b
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func getControlString(ctx kern.ExprContext, name string) (s string, exists bool) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
var v any
|
||||
if v, exists = globalCtx.GetVar(name); exists {
|
||||
s, exists = v.(string)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getControlString(name string) (s string, exists bool) {
|
||||
var v any
|
||||
if v, exists = globalCtx.GetVar(name); exists {
|
||||
s, exists = v.(string)
|
||||
}
|
||||
func InitGlobal() (ctx kern.ExprContext) {
|
||||
ctx = NewSimpleStoreWithoutGlobalContext()
|
||||
kern.InitDefaultVars(ctx)
|
||||
ImportBuiltinsFuncs(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
globalCtx = NewSimpleStore()
|
||||
initDefaultVars(globalCtx)
|
||||
ImportBuiltinsFuncs(globalCtx)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
//go:build graph
|
||||
|
||||
// graph.go
|
||||
package expr
|
||||
|
||||
@@ -53,7 +55,7 @@ func (r *Reticle) computeCharWidth() {
|
||||
if v := ref.node.value(); v != nil {
|
||||
ref.label = fmt.Sprintf("%v", v)
|
||||
} else {
|
||||
ref.label = ref.node.source()
|
||||
ref.label = ref.node.Source()
|
||||
}
|
||||
r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets
|
||||
}
|
||||
|
||||
+20
-16
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// helpers.go
|
||||
@@ -9,13 +9,17 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
)
|
||||
|
||||
func EvalString(ctx ExprContext, source string) (result any, err error) {
|
||||
var tree *ast
|
||||
func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
|
||||
var tree *scan.Ast
|
||||
|
||||
r := strings.NewReader(source)
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
parser := NewParser()
|
||||
|
||||
if tree, err = parser.Parse(scanner); err == nil {
|
||||
@@ -34,21 +38,21 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
|
||||
}
|
||||
|
||||
func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||
ctx := NewSimpleStore()
|
||||
ctx := NewSimpleStoreWithoutGlobalContext()
|
||||
for _, arg := range args {
|
||||
if isFunc(arg.Value) {
|
||||
if f, ok := arg.Value.(FuncTemplate); ok {
|
||||
functor := NewGolangFunctor(f)
|
||||
if util.IsFunc(arg.Value) {
|
||||
if f, ok := arg.Value.(kern.FuncTemplate); ok {
|
||||
functor := kern.NewGolangFunctor(f)
|
||||
// ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
||||
ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0),
|
||||
ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlagDef(kern.ParamValue, kern.PfDefault|kern.PfRepeat, 0),
|
||||
})
|
||||
} else {
|
||||
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
||||
}
|
||||
} else if integer, ok := anyInteger(arg.Value); ok {
|
||||
} else if integer, ok := kern.AnyInteger(arg.Value); ok {
|
||||
ctx.SetVar(arg.Name, integer)
|
||||
} else if float, ok := anyFloat(arg.Value); ok {
|
||||
} else if float, ok := kern.AnyFloat(arg.Value); ok {
|
||||
ctx.SetVar(arg.Name, float)
|
||||
} else if _, ok := arg.Value.(string); ok {
|
||||
ctx.SetVar(arg.Name, arg.Value)
|
||||
@@ -65,9 +69,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
|
||||
var tree *ast
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
|
||||
var tree *scan.Ast
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
parser := NewParser()
|
||||
|
||||
if tree, err = parser.Parse(scanner); err == nil {
|
||||
@@ -76,7 +80,7 @@ func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func EvalFile(ctx ExprContext, filePath string) (result any, err error) {
|
||||
func EvalFile(ctx kern.ExprContext, filePath string) (result any, err error) {
|
||||
var fh *os.File
|
||||
if fh, err = os.Open(filePath); err != nil {
|
||||
return nil, err
|
||||
|
||||
+19
-15
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// 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,19 +86,20 @@ 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) {
|
||||
fullPath := path.Join(dir, filename)
|
||||
if isFile(fullPath) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
|
||||
subdir := strings.TrimSuffix(filename, suffix)
|
||||
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
// subdir := strings.TrimSuffix(filename, suffix)
|
||||
// if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||
// filePath = fullPath
|
||||
// break
|
||||
// }
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -106,7 +110,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
|
||||
}
|
||||
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
// 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 NewIntIteratorA(args ...any) (it *IntIterator, err error) {
|
||||
return NewIntIterator(args)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+36
-6
@@ -1,25 +1,55 @@
|
||||
// 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 NewFormalIterator(value any) (it kern.Iterator) {
|
||||
if value == nil {
|
||||
it = NewArrayIterator([]any{})
|
||||
} else {
|
||||
it = NewArrayIterator([]any{value})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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.LinkedList:
|
||||
it = NewLinkedListIterator(v, nil)
|
||||
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
@@ -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
@@ -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")
|
||||
// }
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// bind-go-function.go
|
||||
package kern
|
||||
|
||||
// ---- Linking with Go functions
|
||||
type GolangFunctor struct {
|
||||
BaseFunctor
|
||||
f FuncTemplate
|
||||
}
|
||||
|
||||
func NewGolangFunctor(f FuncTemplate) *GolangFunctor {
|
||||
return &GolangFunctor{f: f}
|
||||
}
|
||||
|
||||
func (functor *GolangFunctor) TypeName() string {
|
||||
return "GoFunctor"
|
||||
}
|
||||
|
||||
func (functor *GolangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
return functor.f(ctx, name, args)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// string.go
|
||||
package kern
|
||||
|
||||
func IsBool(v any) (ok bool) {
|
||||
_, ok = v.(bool)
|
||||
return ok
|
||||
}
|
||||
|
||||
func ToBool(v any) (b bool, ok bool) {
|
||||
ok = true
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
b = len(x) > 0
|
||||
case float64:
|
||||
b = x != 0.0
|
||||
case int64:
|
||||
b = x != 0
|
||||
case bool:
|
||||
b = x
|
||||
default:
|
||||
ok = false
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// clone-value.go
|
||||
package kern
|
||||
|
||||
func Clone(v any) (c any) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
switch unboxed := v.(type) {
|
||||
case int64:
|
||||
c = unboxed
|
||||
case float64:
|
||||
c = unboxed
|
||||
case string:
|
||||
c = unboxed
|
||||
case bool:
|
||||
c = unboxed
|
||||
case *ListType:
|
||||
c = unboxed.Clone()
|
||||
case *DictType:
|
||||
c = unboxed.Clone()
|
||||
case *LinkedList:
|
||||
c = unboxed.Clone()
|
||||
default:
|
||||
c = v
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
@@ -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"
|
||||
@@ -20,4 +20,5 @@ const (
|
||||
TypeDict = "dict"
|
||||
TypeListOf = "list-of-"
|
||||
TypeListOfStrings = "list-of-strings"
|
||||
TypeLinkedList = "linked-list"
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
package kern
|
||||
|
||||
import "reflect"
|
||||
|
||||
func Equal(value1, value2 any) (equal bool) {
|
||||
if value1 == nil && value2 == nil {
|
||||
equal = true
|
||||
} else if value1 == nil || value2 == nil {
|
||||
equal = false
|
||||
} else if IsBool(value1) && IsBool(value2) {
|
||||
equal = value1.(bool) == value2.(bool)
|
||||
} else if IsList(value1) && IsList(value2) {
|
||||
ls1 := value1.(*ListType)
|
||||
ls2 := value2.(*ListType)
|
||||
equal = ls1.Equals(*ls2)
|
||||
} else if IsDict(value1) && IsDict(value2) {
|
||||
d1 := value1.(*DictType)
|
||||
d2 := value2.(*DictType)
|
||||
equal = d1.Equals(*d2)
|
||||
} else if IsLinkedList(value1) && IsLinkedList(value2) {
|
||||
ll1 := value1.(*LinkedList)
|
||||
ll2 := value2.(*LinkedList)
|
||||
equal = ll1.Equals(ll2)
|
||||
} else if IsInteger(value1) && IsInteger(value2) {
|
||||
equal = value1.(int64) == value2.(int64)
|
||||
} else if IsString(value1) && IsString(value2) {
|
||||
equal = value1.(string) == value2.(string)
|
||||
} else if IsFloat(value1) && IsFloat(value2) {
|
||||
equal = value1.(float64) == value2.(float64)
|
||||
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
|
||||
if eq, err := CmpAnyFract(value1, value2); err == nil {
|
||||
equal = eq == 0
|
||||
}
|
||||
} else if !reflect.DeepEqual(value1, value2) {
|
||||
equal = false
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// context-helpers.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
||||
func CloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
||||
if sourceCtx != nil {
|
||||
clonedCtx = sourceCtx.Clone()
|
||||
}
|
||||
@@ -25,8 +25,8 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
||||
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
|
||||
}
|
||||
|
||||
func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
||||
func ExportObjects(destCtx, sourceCtx ExprContext) {
|
||||
exportAll := CtrlIsEnabled(sourceCtx, ControlExportAll)
|
||||
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
||||
// Export variables
|
||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
|
||||
@@ -44,6 +44,6 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||
|
||||
func exportObjectsToParent(sourceCtx ExprContext) {
|
||||
if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
|
||||
exportObjects(parentCtx, sourceCtx)
|
||||
ExportObjects(parentCtx, sourceCtx)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// control.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import "strings"
|
||||
|
||||
// Preset control variables
|
||||
const (
|
||||
@@ -16,7 +18,7 @@ const (
|
||||
|
||||
// Other control variables
|
||||
const (
|
||||
control_export_all = "_export_all"
|
||||
ControlExportAll = "_export_all"
|
||||
)
|
||||
|
||||
// Initial values
|
||||
@@ -24,13 +26,34 @@ const (
|
||||
init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
|
||||
)
|
||||
|
||||
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
|
||||
var v any
|
||||
var exists bool
|
||||
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
v, exists = globalCtx.GetVar(name)
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
if b, ok := v.(bool); ok {
|
||||
status = b
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func SetCtrl(ctx ExprContext, name string, value any) (current any) {
|
||||
current, _ = ctx.GetVar(name)
|
||||
ctx.UnsafeSetVar(name, value)
|
||||
return
|
||||
}
|
||||
|
||||
func initDefaultVars(ctx ExprContext) {
|
||||
func InitDefaultVars(ctx ExprContext) {
|
||||
if _, exists := ctx.GetVar(ControlPreset); exists {
|
||||
return
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// dict-type.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -10,8 +10,15 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const DictTypeName = "dict"
|
||||
|
||||
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 +39,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 {
|
||||
@@ -69,9 +76,11 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
||||
|
||||
sb.WriteString(nest)
|
||||
if key, ok := name.(string); ok {
|
||||
sb.WriteString(string('"') + key + string('"'))
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(key)
|
||||
sb.WriteByte('"')
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", name))
|
||||
fmt.Fprintf(sb, "%v", name)
|
||||
}
|
||||
sb.WriteString(": ")
|
||||
if f, ok := value.(Formatter); ok {
|
||||
@@ -79,7 +88,7 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
||||
} else if _, ok = value.(Functor); ok {
|
||||
sb.WriteString("func(){}")
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", value))
|
||||
fmt.Fprintf(sb, "%v", value)
|
||||
}
|
||||
}
|
||||
sb.WriteByte('\n')
|
||||
@@ -103,17 +112,19 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
if s, ok := key.(string); ok {
|
||||
sb.WriteString(string('"') + s + string('"'))
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", key))
|
||||
fmt.Fprintf(&sb, "%v", key)
|
||||
}
|
||||
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))
|
||||
fmt.Fprintf(&sb, "%#v", value)
|
||||
}
|
||||
}
|
||||
sb.WriteByte('}')
|
||||
@@ -126,10 +137,10 @@ func (dict *DictType) String() string {
|
||||
}
|
||||
|
||||
func (dict *DictType) TypeName() string {
|
||||
return "dict"
|
||||
return DictTypeName
|
||||
}
|
||||
|
||||
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,15 +149,24 @@ 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
|
||||
(*c)[k] = Clone(v)
|
||||
}
|
||||
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 +174,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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// expr-context.go
|
||||
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)
|
||||
UnsafeSetVar(varName string, value any)
|
||||
|
||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||
VarCount() int
|
||||
DeleteVar(varName string)
|
||||
|
||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||
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)
|
||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||
|
||||
ToDict() (dict *DictType)
|
||||
ToString(opt FmtOpt) string
|
||||
}
|
||||
|
||||
func ContextToDict(ctx ExprContext) (dict *DictType) {
|
||||
var keys []string
|
||||
// Variables
|
||||
keys = ctx.EnumVars(nil)
|
||||
vars := MakeDict()
|
||||
for _, key := range keys {
|
||||
value, _ := ctx.GetVar(key)
|
||||
vars.SetItem(key, value)
|
||||
}
|
||||
|
||||
// Functions
|
||||
keys = ctx.EnumFuncs(func(name string) bool { return true })
|
||||
funcs := MakeDict()
|
||||
for _, key := range keys {
|
||||
funcInfo, _ := ctx.GetFuncInfo(key)
|
||||
funcs.SetItem(key, funcInfo)
|
||||
}
|
||||
|
||||
dict = MakeDict()
|
||||
dict.SetItem("vars", vars)
|
||||
dict.SetItem("funcs", funcs)
|
||||
return
|
||||
}
|
||||
|
||||
func ContextToString(ctx ExprContext, opt FmtOpt) string {
|
||||
dict := ctx.ToDict()
|
||||
return dict.ToString(opt)
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// expr-function.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
// ---- Functor interface
|
||||
type Functor interface {
|
||||
@@ -37,3 +37,8 @@ type ExprFunc interface {
|
||||
PrepareCall(name string, actualParams map[string]any) (err error)
|
||||
AllocContext(parentCtx ExprContext) (ctx ExprContext)
|
||||
}
|
||||
|
||||
func IsFunctor(v any) (ok bool) {
|
||||
_, ok = v.(Functor)
|
||||
return
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// expr.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
// ----Expression interface
|
||||
type Expr interface {
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// float.go
|
||||
package kern
|
||||
|
||||
func IsFloat(v any) (ok bool) {
|
||||
_, ok = v.(float64)
|
||||
return ok
|
||||
}
|
||||
|
||||
func AnyFloat(v any) (float float64, ok bool) {
|
||||
ok = true
|
||||
switch floatval := v.(type) {
|
||||
case float32:
|
||||
float = float64(floatval)
|
||||
case float64:
|
||||
float = floatval
|
||||
default:
|
||||
ok = false
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
// 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"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||
|
||||
@@ -46,7 +49,19 @@ type Formatter interface {
|
||||
ToString(options FmtOpt) string
|
||||
}
|
||||
|
||||
func getFormatted(v any, opt FmtOpt) (text string) {
|
||||
func Format(sb *strings.Builder, item any, opt FmtOpt) {
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(opt))
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||
if v == nil {
|
||||
text = "(nil)"
|
||||
} else if s, ok := v.(string); ok {
|
||||
@@ -1,8 +1,8 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// fraction-type.go
|
||||
package expr
|
||||
package kern
|
||||
|
||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
||||
|
||||
@@ -18,12 +18,12 @@ type FractionType struct {
|
||||
num, den int64
|
||||
}
|
||||
|
||||
func newFraction(num, den int64) *FractionType {
|
||||
func NewFraction(num, den int64) *FractionType {
|
||||
num, den = simplifyIntegers(num, den)
|
||||
return &FractionType{num, den}
|
||||
}
|
||||
|
||||
func float64ToFraction(f float64) (fract *FractionType, err error) {
|
||||
func Float64ToFraction(f float64) (fract *FractionType, err error) {
|
||||
var sign string
|
||||
intPart, decPart := math.Modf(f)
|
||||
if decPart < 0.0 {
|
||||
@@ -33,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
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// func-info.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// --- Functions
|
||||
|
||||
// FuncInfo implements expr.ExprFunc
|
||||
type FuncInfo struct {
|
||||
name string
|
||||
minArgs int
|
||||
maxArgs int
|
||||
functor Functor
|
||||
formalParams []ExprFuncParam
|
||||
returnType string
|
||||
}
|
||||
|
||||
func NewFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *FuncInfo, err error) {
|
||||
var minArgs = 0
|
||||
var maxArgs = 0
|
||||
for _, p := range params {
|
||||
if maxArgs == -1 {
|
||||
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
|
||||
}
|
||||
if p.IsDefault() || p.IsOptional() {
|
||||
maxArgs++
|
||||
} else if maxArgs == minArgs {
|
||||
minArgs++
|
||||
maxArgs++
|
||||
} else {
|
||||
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
|
||||
}
|
||||
if p.IsRepeat() {
|
||||
minArgs--
|
||||
maxArgs = -1
|
||||
}
|
||||
}
|
||||
|
||||
info = &FuncInfo{
|
||||
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
|
||||
}
|
||||
functor.SetFunc(info)
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (info *FuncInfo) Params() []ExprFuncParam {
|
||||
return info.formalParams
|
||||
}
|
||||
|
||||
func (info *FuncInfo) ReturnType() string {
|
||||
return info.returnType
|
||||
}
|
||||
|
||||
func (info *FuncInfo) ToString(opt FmtOpt) string {
|
||||
var sb strings.Builder
|
||||
if len(info.Name()) == 0 {
|
||||
sb.WriteString("func")
|
||||
} else {
|
||||
sb.WriteString(info.Name())
|
||||
}
|
||||
sb.WriteByte('(')
|
||||
if info.formalParams != nil {
|
||||
for i, p := range info.formalParams {
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(p.Name())
|
||||
|
||||
if p.IsDefault() {
|
||||
sb.WriteByte('=')
|
||||
if s, ok := p.DefaultValue().(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if info.maxArgs < 0 {
|
||||
sb.WriteString(" ...")
|
||||
}
|
||||
sb.WriteString("):")
|
||||
if len(info.returnType) > 0 {
|
||||
sb.WriteString(info.returnType)
|
||||
} else {
|
||||
sb.WriteString(TypeAny)
|
||||
}
|
||||
sb.WriteString("{}")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (info *FuncInfo) Name() string {
|
||||
return info.name
|
||||
}
|
||||
|
||||
func (info *FuncInfo) MinArgs() int {
|
||||
return info.minArgs
|
||||
}
|
||||
|
||||
func (info *FuncInfo) MaxArgs() int {
|
||||
return info.maxArgs
|
||||
}
|
||||
|
||||
func (info *FuncInfo) Functor() Functor {
|
||||
return info.functor
|
||||
}
|
||||
|
||||
func (info *FuncInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
|
||||
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
|
||||
ctx = defCtx.Clone()
|
||||
ctx.SetParent(defCtx)
|
||||
} else {
|
||||
ctx = parentCtx.Clone()
|
||||
ctx.SetParent(parentCtx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (info *FuncInfo) ParamSpec(paramName string) ExprFuncParam {
|
||||
for _, spec := range info.formalParams {
|
||||
if spec.Name() == paramName {
|
||||
return spec
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (info *FuncInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
|
||||
passedCount := len(actualParams)
|
||||
if info.MinArgs() > passedCount {
|
||||
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||
return
|
||||
}
|
||||
|
||||
if passedCount < len(info.formalParams) {
|
||||
for _, p := range info.formalParams {
|
||||
if _, exists := actualParams[p.Name()]; !exists {
|
||||
if !p.IsDefault() {
|
||||
break
|
||||
}
|
||||
if p.IsRepeat() {
|
||||
varArgs := make([]any, 1)
|
||||
varArgs[0] = p.DefaultValue()
|
||||
actualParams[p.Name()] = varArgs
|
||||
} else {
|
||||
actualParams[p.Name()] = p.DefaultValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
|
||||
err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// function.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ---- Function templates
|
||||
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
|
||||
|
||||
type DeepFuncTemplate func(a, b any) (eq bool, err error)
|
||||
|
||||
// ---- Common functor definition
|
||||
type BaseFunctor struct {
|
||||
info ExprFunc
|
||||
}
|
||||
|
||||
func (functor *BaseFunctor) ToString(opt FmtOpt) (s string) {
|
||||
if functor.info != nil {
|
||||
s = functor.info.ToString(opt)
|
||||
} else {
|
||||
s = "func(){}"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (functor *BaseFunctor) GetParams() (params []ExprFuncParam) {
|
||||
if functor.info != nil {
|
||||
return functor.info.Params()
|
||||
} else {
|
||||
return []ExprFuncParam{}
|
||||
}
|
||||
}
|
||||
|
||||
func (functor *BaseFunctor) SetFunc(info ExprFunc) {
|
||||
functor.info = info
|
||||
}
|
||||
|
||||
func (functor *BaseFunctor) GetFunc() ExprFunc {
|
||||
return functor.info
|
||||
}
|
||||
|
||||
func (functor *BaseFunctor) GetDefinitionContext() ExprContext {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ---- Function Parameters
|
||||
type FuncParamFlags uint16
|
||||
|
||||
const (
|
||||
PfDefault FuncParamFlags = 1 << iota
|
||||
PfOptional
|
||||
PfRepeat
|
||||
)
|
||||
|
||||
type funcParamInfo struct {
|
||||
name string
|
||||
flags FuncParamFlags
|
||||
defaultValue any
|
||||
}
|
||||
|
||||
func NewFuncParam(name string) ExprFuncParam {
|
||||
return &funcParamInfo{name: name}
|
||||
}
|
||||
|
||||
func NewFuncParamFlag(name string, flags FuncParamFlags) ExprFuncParam {
|
||||
return &funcParamInfo{name: name, flags: flags}
|
||||
}
|
||||
|
||||
func NewFuncParamFlagDef(name string, flags FuncParamFlags, defValue any) *funcParamInfo {
|
||||
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) Name() string {
|
||||
return param.name
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) Type() string {
|
||||
return TypeAny
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) IsDefault() bool {
|
||||
return (param.flags & PfDefault) != 0
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) IsOptional() bool {
|
||||
return (param.flags & PfOptional) != 0
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) IsRepeat() bool {
|
||||
return (param.flags & PfRepeat) != 0
|
||||
}
|
||||
|
||||
func (param *funcParamInfo) DefaultValue() any {
|
||||
return param.defaultValue
|
||||
}
|
||||
|
||||
func initActualParams(ctx ExprContext, info ExprFunc, callTerm Term) (actualParams map[string]any, err error) {
|
||||
var varArgs []any
|
||||
var varName string
|
||||
|
||||
namedParamsStarted := false
|
||||
|
||||
formalParams := info.Params()
|
||||
actualParams = make(map[string]any, len(formalParams))
|
||||
if callTerm == nil {
|
||||
return
|
||||
}
|
||||
|
||||
childCount := callTerm.GetChildCount()
|
||||
for i := range childCount {
|
||||
tree := callTerm.GetChild(i)
|
||||
// for i, tree := range callTerm.Children() {
|
||||
var paramValue any
|
||||
paramCtx := ctx.Clone()
|
||||
if paramValue, err = tree.Compute(paramCtx); err != nil {
|
||||
break
|
||||
}
|
||||
if paramName, namedParam := GetAssignVarName(tree); namedParam {
|
||||
if info.ParamSpec(paramName) == nil {
|
||||
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
|
||||
break
|
||||
}
|
||||
actualParams[paramName] = paramValue
|
||||
namedParamsStarted = true
|
||||
} else if !namedParamsStarted {
|
||||
if varArgs != nil {
|
||||
varArgs = append(varArgs, paramValue)
|
||||
} else if i < len(formalParams) {
|
||||
spec := formalParams[i]
|
||||
if spec.IsRepeat() {
|
||||
varArgs = make([]any, 0, childCount-i)
|
||||
varArgs = append(varArgs, paramValue)
|
||||
varName = spec.Name()
|
||||
} else {
|
||||
actualParams[spec.Name()] = paramValue
|
||||
}
|
||||
} else {
|
||||
err = ErrTooManyParams(info.Name(), len(formalParams), childCount)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if varArgs != nil {
|
||||
actualParams[varName] = varArgs
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
|
||||
// passedCount := len(actualParams)
|
||||
// if info.MinArgs() > passedCount {
|
||||
// err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
||||
// return
|
||||
// }
|
||||
|
||||
// if passedCount < len(info.formalParams) {
|
||||
// for _, p := range info.formalParams {
|
||||
// if _, exists := actualParams[p.Name()]; !exists {
|
||||
// if !p.IsDefault() {
|
||||
// break
|
||||
// }
|
||||
// if p.IsRepeat() {
|
||||
// varArgs := make([]any, 1)
|
||||
// varArgs[0] = p.DefaultValue()
|
||||
// actualParams[p.Name()] = varArgs
|
||||
// } else {
|
||||
// actualParams[p.Name()] = p.DefaultValue()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
|
||||
// err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// ----- Call a function ---
|
||||
|
||||
// func getAssignVarName(t *term) (name string, ok bool) {
|
||||
// if ok = t.symbol() == SymEqual; ok {
|
||||
// name = t.children[0].source()
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func GetAssignVarName(t Term) (name string, ok bool) {
|
||||
if ok = t.IsAssign(); ok {
|
||||
name = t.GetChildSource(0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm Term) (result any, err error) {
|
||||
var actualParams map[string]any
|
||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
||||
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
|
||||
ctx := info.AllocContext(parentCtx)
|
||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
||||
functor := info.Functor()
|
||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
||||
exportObjectsToParent(ctx)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
|
||||
var actualParams map[string]any
|
||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
||||
functor := info.Functor()
|
||||
actualParams = BindActualParams(functor, args)
|
||||
ctx := info.AllocContext(parentCtx)
|
||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
||||
exportObjectsToParent(ctx)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
|
||||
//var actualParams map[string]any
|
||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
||||
functor := info.Functor()
|
||||
ctx := info.AllocContext(parentCtx)
|
||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
||||
exportObjectsToParent(ctx)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
|
||||
if value, exists = args[paramName]; !exists {
|
||||
if paramNum > 0 && paramNum <= len(args) {
|
||||
value, exists = args["arg"+strconv.Itoa(paramNum)]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func BindActualParams(functor Functor, args []any) (actualParams map[string]any) {
|
||||
formalParams := functor.GetParams()
|
||||
actualParams = make(map[string]any, len(args))
|
||||
for i, arg := range args {
|
||||
if i < len(formalParams) {
|
||||
actualParams[formalParams[i].Name()] = arg
|
||||
} else {
|
||||
actualParams["arg"+strconv.Itoa(i+1)] = arg
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// linked-list-type.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const LinkedListTypeName = "lisked-list"
|
||||
const MaxUint64Allowed = uint64(9_223_372_036_854_775_807)
|
||||
|
||||
func IsLinkedList(v any) (ok bool) {
|
||||
_, ok = v.(*LinkedList)
|
||||
return ok
|
||||
}
|
||||
|
||||
func NewLinkedListA(listAny ...any) (list *LinkedList) {
|
||||
if listAny == nil {
|
||||
listAny = []any{}
|
||||
}
|
||||
list = NewLinkedList()
|
||||
for _, item := range listAny {
|
||||
list.PushBack(FixAnyNumber(item))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LinkedListFromStrings(stringList []string) (list *LinkedList) {
|
||||
list = NewLinkedList()
|
||||
for _, s := range stringList {
|
||||
list.PushBack(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) ToString(opt FmtOpt) (s string) {
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("[<")
|
||||
if ls.Len() > 0 {
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(nest)
|
||||
}
|
||||
|
||||
i := 0
|
||||
for item := ls.FirstNode(); item != nil; item = item.Next() {
|
||||
if i > 0 {
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteString(",\n")
|
||||
sb.WriteString(nest)
|
||||
} else {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
// data := item.Data()
|
||||
// if s, ok := data.(string); ok {
|
||||
// sb.WriteByte('"')
|
||||
// sb.WriteString(s)
|
||||
// sb.WriteByte('"')
|
||||
// } else if formatter, ok := data.(Formatter); ok {
|
||||
// sb.WriteString(formatter.ToString(innerOpt))
|
||||
// } else {
|
||||
// fmt.Fprintf(&sb, "%v", data)
|
||||
// }
|
||||
Format(&sb, item.Data(), innerOpt)
|
||||
i++
|
||||
}
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(strings.Repeat(" ", indent))
|
||||
}
|
||||
}
|
||||
sb.WriteString(">]")
|
||||
s = sb.String()
|
||||
if flags&Truncate != 0 && len(s) > TruncateSize {
|
||||
s = TruncateString(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) String() string {
|
||||
return ls.ToString(0)
|
||||
}
|
||||
|
||||
func (ls *LinkedList) TypeName() string {
|
||||
return LinkedListTypeName
|
||||
}
|
||||
|
||||
// func (ls *LinkedList) Contains(t *ListType) (answer bool) {
|
||||
// if len(*ls) >= len(*t) {
|
||||
// answer = true
|
||||
// for _, item := range *t {
|
||||
// if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (ls1 *LinkedList) Equals(ls2 *LinkedList) (answer bool) {
|
||||
if ls2 != nil && ls1.Len() == ls2.Len() {
|
||||
answer = true
|
||||
i2 := ls2.FirstNode()
|
||||
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||
if !Equal(i1.Data(), i2.Data()) {
|
||||
answer = false
|
||||
break
|
||||
}
|
||||
i2 = i2.Next()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *LinkedList) Clone() (ls2 *LinkedList) {
|
||||
ls2 = NewLinkedListA()
|
||||
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||
ls2.PushBack(Clone(i1.Data()))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
// 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 int
|
||||
first *ListNode
|
||||
last *ListNode
|
||||
}
|
||||
|
||||
func NewLinkedList() (list *LinkedList) {
|
||||
list = &LinkedList{0, nil, nil}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) Len() int {
|
||||
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 int) (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 range index {
|
||||
current = current.next
|
||||
}
|
||||
node = current
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) At(index int) (data interface{}, err error) {
|
||||
node, err := ls.NodeAt(index)
|
||||
if err == nil {
|
||||
data = node.data
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) Insert(index int, data any) *ListNode {
|
||||
var prev *ListNode
|
||||
|
||||
prev = nil
|
||||
current := ls.first
|
||||
for pos := 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)
|
||||
}
|
||||
}
|
||||
|
||||
func (ls *LinkedList) Sub(start, end int) (subList *LinkedList) {
|
||||
subList = NewLinkedList()
|
||||
if node, err := ls.NodeAt(start); err == nil {
|
||||
for i := start; i < end && node != nil; i++ {
|
||||
subList.PushBack(node.data)
|
||||
node = node.next
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// type TraverseOperator func(index int, elem interface{}, userData interface{}) (err error)
|
||||
|
||||
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
|
||||
// index := int(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 := int(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 int, userData interface{})
|
||||
|
||||
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count int) {
|
||||
// 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 int, 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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -75,15 +73,7 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(innerOpt))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", item))
|
||||
}
|
||||
Format(&sb, item, innerOpt)
|
||||
}
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
@@ -106,22 +96,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) {
|
||||
func (ls *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*ls) >= len(*t) {
|
||||
answer = true
|
||||
for _, item := range *t {
|
||||
if answer = ls.indexDeepSameCmp(item) >= 0; !answer {
|
||||
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -129,12 +108,15 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
||||
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls) == len(ls2) {
|
||||
answer = true
|
||||
for index, i1 := range *ls1 {
|
||||
// if i1 != (ls2)[index] {
|
||||
if !reflect.DeepEqual(i1, ls2[index]) {
|
||||
for index, i1 := range *ls {
|
||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
||||
// answer = false
|
||||
// break
|
||||
// }
|
||||
if !Equal(i1, ls2[index]) {
|
||||
answer = false
|
||||
break
|
||||
}
|
||||
@@ -143,12 +125,21 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||
func (ls1 *ListType) Clone() (ls2 *ListType) {
|
||||
ls := make(ListType, len(*ls1))
|
||||
for i, item := range *ls1 {
|
||||
ls[i] = Clone(item)
|
||||
}
|
||||
ls2 = &ls
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
var eq bool
|
||||
var err error
|
||||
index = -1
|
||||
for i, item := range *list {
|
||||
if eq, err = deepSame(item, target, sameContent); err != nil {
|
||||
for i, item := range *ls {
|
||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
||||
break
|
||||
} else if eq {
|
||||
index = i
|
||||
@@ -158,13 +149,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 +164,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 +189,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 (ls *ListType) SetItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*ls)) {
|
||||
(*ls)[index] = value
|
||||
} else {
|
||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*list)-1)
|
||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) appendItem(value any) {
|
||||
*list = append(*list, value)
|
||||
func (ls *ListType) AppendItem(value any) {
|
||||
*ls = append(*ls, value)
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
// 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 FixAnyNumber(v any) (fixed any) {
|
||||
switch unboxed := v.(type) {
|
||||
case int:
|
||||
fixed = int64(unboxed)
|
||||
case int8:
|
||||
fixed = int64(unboxed)
|
||||
case int16:
|
||||
fixed = int64(unboxed)
|
||||
case int32:
|
||||
fixed = int64(unboxed)
|
||||
case uint:
|
||||
fixed = int64(unboxed)
|
||||
case uint8:
|
||||
fixed = int64(unboxed)
|
||||
case uint16:
|
||||
fixed = int64(unboxed)
|
||||
case uint32:
|
||||
fixed = int64(unboxed)
|
||||
case uint64:
|
||||
if unboxed <= MaxUint64Allowed {
|
||||
fixed = int64(unboxed)
|
||||
} else {
|
||||
fixed = float64(unboxed)
|
||||
}
|
||||
case float32:
|
||||
fixed = float64(unboxed)
|
||||
default:
|
||||
fixed = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ToGoInt(value any, description string) (i int, err error) {
|
||||
if valueInt64, ok := value.(int64); ok {
|
||||
i = int(valueInt64)
|
||||
} else if valueInt, ok := value.(int); ok {
|
||||
i = valueInt
|
||||
} else {
|
||||
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ToGoInt64(value any, description string) (i int64, err error) {
|
||||
if valueInt64, ok := value.(int64); ok {
|
||||
i = valueInt64
|
||||
} else if valueInt, ok := value.(int); ok {
|
||||
i = int64(valueInt)
|
||||
} else {
|
||||
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// string.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func IsString(v any) (ok bool) {
|
||||
_, ok = v.(string)
|
||||
return ok
|
||||
}
|
||||
|
||||
func ToGoString(value any, description string) (s string, err error) {
|
||||
if s, ok := value.(string); ok {
|
||||
return s, nil
|
||||
} else {
|
||||
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// term.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Term interface {
|
||||
fmt.Stringer
|
||||
// Children() []Term
|
||||
Source() string
|
||||
GetChildCount() (count int)
|
||||
GetChild(index int) Term
|
||||
GetChildSource(index int) string
|
||||
Compute(ctx ExprContext) (result any, err error)
|
||||
IsAssign() bool
|
||||
Errorf(template string, args ...any) (err error)
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
//go:build darwin
|
||||
|
||||
// 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
@@ -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
@@ -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
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// list-iterator.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type LinkedListIterator struct {
|
||||
a *kern.LinkedList
|
||||
count int64
|
||||
index int64
|
||||
current *kern.ListNode
|
||||
}
|
||||
|
||||
func NewLinkedListIterator(list *kern.LinkedList, args []any) (it *LinkedListIterator) {
|
||||
it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) String() string {
|
||||
var l = int64(0)
|
||||
if it.a != nil {
|
||||
l = int64(it.a.Len())
|
||||
}
|
||||
return fmt.Sprintf("$([<#%d>])", l)
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) TypeName() string {
|
||||
return "LinkedListIterator"
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) HasOperation(name string) bool {
|
||||
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
|
||||
return yes
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) 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 *LinkedListIterator) Current() (item any, err error) {
|
||||
if it.current != nil {
|
||||
item = it.current.Data()
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Next() (item any, err error) {
|
||||
if it.current != nil {
|
||||
item = it.current.Data()
|
||||
it.current = it.current.Next()
|
||||
it.index++
|
||||
it.count++
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Index() int64 {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Count() int64 {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Reset() error {
|
||||
it.current = it.a.FirstNode()
|
||||
it.index = -1
|
||||
it.count = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Clean() error {
|
||||
return nil
|
||||
}
|
||||
+31
-27
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
+25
-14
@@ -1,32 +1,43 @@
|
||||
// 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
|
||||
var keyValue any
|
||||
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
|
||||
if kern.IsInteger(keyValue) || kern.IsString(keyValue) {
|
||||
items[keyValue] = param
|
||||
} else {
|
||||
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
v = &items
|
||||
|
||||
+20
-15
@@ -1,30 +1,35 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-expr.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- expr term
|
||||
func newExprTerm(root *term) *term {
|
||||
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.source(), root)
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalExpr,
|
||||
func newExprTerm(root *scan.Term) *scan.Term {
|
||||
tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymExpression, root.Source(), root)
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalExpr,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval expr
|
||||
func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if expr, ok := opTerm.value().(*term); ok {
|
||||
v, err = expr.compute(ctx)
|
||||
func evalExpr(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if ast, ok := opTerm.Value().(*scan.Term); ok {
|
||||
v, err = ast.Compute(ctx)
|
||||
} else {
|
||||
err = fmt.Errorf("expression expected, got %T", opTerm.value())
|
||||
err = fmt.Errorf("expression expected, got %T", opTerm.Value())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+42
-35
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-func.go
|
||||
@@ -6,27 +6,34 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- function call term
|
||||
func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: args,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalFuncCall,
|
||||
func newFuncCallTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
var pos scan.TermPosition = scan.PosLeaf
|
||||
if len(args) > 0 {
|
||||
pos = scan.PosMultifix
|
||||
}
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: args,
|
||||
Position: pos,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalFuncCall,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func call
|
||||
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
// name, _ := opTerm.tk.Value.(string)
|
||||
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
|
||||
// for i, tree := range opTerm.children {
|
||||
// name, _ := opTerm.Tk.Value.(string)
|
||||
// params := make([]any, len(opTerm.Children), len(opTerm.Children)+5)
|
||||
// for i, tree := range opTerm.Children {
|
||||
// var param any
|
||||
// if param, err = tree.compute(ctx); err != nil {
|
||||
// if param, err = tree.Compute(ctx); err != nil {
|
||||
// break
|
||||
// }
|
||||
// params[i] = param
|
||||
@@ -38,42 +45,42 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
// return
|
||||
// }
|
||||
|
||||
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
name, _ := opTerm.tk.Value.(string)
|
||||
v, err = CallFunctionByTerm(ctx, name, opTerm)
|
||||
func evalFuncCall(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
name, _ := opTerm.Tk.Value.(string)
|
||||
v, err = kern.CallFunctionByTerm(ctx, name, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
// -------- function definition term
|
||||
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||
return &term{
|
||||
tk: *tk, // value is the expression body
|
||||
parent: nil,
|
||||
children: args, // function params
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalFuncDef,
|
||||
func newFuncDefTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk, // value is the expression body
|
||||
Parent: nil,
|
||||
Children: args, // function params
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalFuncDef,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func def
|
||||
func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
bodySpec := opTerm.value()
|
||||
if expr, ok := bodySpec.(*ast); ok {
|
||||
paramList := make([]ExprFuncParam, 0, len(opTerm.children))
|
||||
for _, param := range opTerm.children {
|
||||
func evalFuncDef(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
bodySpec := opTerm.Value()
|
||||
if ast, ok := bodySpec.(*scan.Ast); ok {
|
||||
paramList := make([]kern.ExprFuncParam, 0, len(opTerm.Children))
|
||||
for _, param := range opTerm.Children {
|
||||
var defValue any
|
||||
flags := paramFlags(0)
|
||||
if len(param.children) > 0 {
|
||||
flags |= PfDefault
|
||||
if defValue, err = param.children[0].compute(ctx); err != nil {
|
||||
flags := kern.FuncParamFlags(0)
|
||||
if len(param.Children) > 0 {
|
||||
flags |= kern.PfDefault
|
||||
if defValue, err = param.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
info := NewFuncParamFlagDef(param.source(), flags, defValue)
|
||||
info := kern.NewFuncParamFlagDef(param.Source(), flags, defValue)
|
||||
paramList = append(paramList, info)
|
||||
}
|
||||
v = newExprFunctor(expr, paramList, ctx)
|
||||
v = newExprFunctor(ast, paramList, ctx)
|
||||
} else {
|
||||
err = errors.New("invalid function definition: the body specification must be an expression")
|
||||
}
|
||||
|
||||
+83
-39
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-iterator.go
|
||||
@@ -7,29 +7,32 @@ package expr
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- iterator term
|
||||
|
||||
func newIteratorTerm(tk *Token, args []*term) *term {
|
||||
tk.Sym = SymIterator
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: args,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalIterator,
|
||||
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||
tk.Sym = scan.SymIterator
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: args,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalIterator,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval iterator
|
||||
|
||||
func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
|
||||
func evalTermArray(ctx kern.ExprContext, terms []*scan.Term) (values []any, err error) {
|
||||
values = make([]any, len(terms))
|
||||
for i, t := range terms {
|
||||
var value any
|
||||
if value, err = t.compute(ctx); err == nil {
|
||||
if value, err = t.Compute(ctx); err == nil {
|
||||
values[i] = value
|
||||
} else {
|
||||
break
|
||||
@@ -38,25 +41,25 @@ func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) {
|
||||
if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
|
||||
func evalFirstChild(ctx kern.ExprContext, iteratorTerm *scan.Term) (value any, err error) {
|
||||
if len(iteratorTerm.Children) < 1 || iteratorTerm.Children[0] == nil {
|
||||
err = iteratorTerm.Errorf("missing the data-source parameter")
|
||||
return
|
||||
}
|
||||
|
||||
value, err = iteratorTerm.children[0].compute(ctx)
|
||||
value, err = iteratorTerm.Children[0].Compute(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
|
||||
if dictAny, ok := firstChildValue.(*DictType); ok {
|
||||
requiredFields := []string{NextName}
|
||||
func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
|
||||
if dictAny, ok := firstChildValue.(*kern.DictType); ok {
|
||||
requiredFields := []string{kern.NextName}
|
||||
fieldsMask := 0b1
|
||||
foundFields := 0
|
||||
ds = make(map[string]Functor)
|
||||
ds = make(map[string]kern.Functor)
|
||||
for keyAny, item := range *dictAny {
|
||||
if key, ok := keyAny.(string); ok {
|
||||
if functor, ok := item.(Functor); ok {
|
||||
if functor, ok := item.(kern.Functor); ok {
|
||||
ds[key] = functor
|
||||
if index := slices.Index(requiredFields, key); index >= 0 {
|
||||
foundFields |= 1 << index
|
||||
@@ -72,15 +75,15 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
|
||||
missingFields = append(missingFields, field)
|
||||
}
|
||||
}
|
||||
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
||||
err = iteratorTerm.Children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var firstChildValue any
|
||||
var ds map[string]Functor
|
||||
var ds map[string]kern.Functor
|
||||
|
||||
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
|
||||
return
|
||||
@@ -95,24 +98,24 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if len(ds) > 0 {
|
||||
var dc *dataCursor
|
||||
dcCtx := ctx.Clone()
|
||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||
if initFunc, exists := ds[kern.InitName]; exists && initFunc != nil {
|
||||
var args []any
|
||||
var resource any
|
||||
if len(opTerm.children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
||||
if len(opTerm.Children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.Children[1:]); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
args = []any{}
|
||||
}
|
||||
|
||||
actualParams := bindActualParams(initFunc, args)
|
||||
actualParams := kern.BindActualParams(initFunc, args)
|
||||
|
||||
initCtx := ctx.Clone()
|
||||
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
||||
if resource, err = initFunc.InvokeNamed(initCtx, kern.InitName, actualParams); err != nil {
|
||||
return
|
||||
}
|
||||
exportObjects(dcCtx, initCtx)
|
||||
kern.ExportObjects(dcCtx, initCtx)
|
||||
dc = NewDataCursor(dcCtx, ds, resource)
|
||||
} else {
|
||||
dc = NewDataCursor(dcCtx, ds, nil)
|
||||
@@ -120,30 +123,71 @@ 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 list, ok := firstChildValue.(*kern.LinkedList); ok {
|
||||
var args []any
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||
v = NewLinkedListIterator(list, args)
|
||||
}
|
||||
} else if intVal, ok := firstChildValue.(int64); ok {
|
||||
var args []any
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
||||
v, err = NewIntIterator(args)
|
||||
}
|
||||
} 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 +196,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)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 newLinkedListTermA(args ...*scan.Term) *scan.Term {
|
||||
// return newLinkedListTerm(0, 0, args)
|
||||
// }
|
||||
|
||||
func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *scan.NewValueToken(row, col, scan.SymLinkedList, "[<>]", args),
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalLinkedList,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- list func
|
||||
func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
list, _ := opTerm.Value().([]*scan.Term)
|
||||
items := kern.NewLinkedList()
|
||||
for _, tree := range list {
|
||||
var param any
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
items.PushBack(param)
|
||||
}
|
||||
if err == nil {
|
||||
v = items
|
||||
}
|
||||
return
|
||||
}
|
||||
+19
-14
@@ -1,32 +1,37 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-list.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- list term
|
||||
func newListTermA(args ...*term) *term {
|
||||
func newListTermA(args ...*scan.Term) *scan.Term {
|
||||
return newListTerm(0, 0, args)
|
||||
}
|
||||
|
||||
func newListTerm(row, col int, args []*term) *term {
|
||||
return &term{
|
||||
tk: *NewValueToken(row, col, SymList, "[]", args),
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalList,
|
||||
func newListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *scan.NewValueToken(row, col, scan.SymList, "[]", args),
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalList,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- list func
|
||||
func evalList(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
list, _ := opTerm.value().([]*term)
|
||||
items := make(ListType, len(list))
|
||||
func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
list, _ := opTerm.Value().([]*scan.Term)
|
||||
items := make(kern.ListType, len(list))
|
||||
for i, tree := range list {
|
||||
var param any
|
||||
if param, err = tree.compute(ctx); err != nil {
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
items[i] = param
|
||||
|
||||
+22
-17
@@ -1,33 +1,38 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-literal.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- literal term
|
||||
func newLiteralTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalLiteral,
|
||||
func newLiteralTerm(tk *scan.Token) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalLiteral,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func
|
||||
func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
v = opTerm.tk.Value
|
||||
func evalLiteral(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v = opTerm.Tk.Value
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymString, newLiteralTerm)
|
||||
registerTermConstructor(SymInteger, newLiteralTerm)
|
||||
registerTermConstructor(SymFloat, newLiteralTerm)
|
||||
registerTermConstructor(SymFraction, newLiteralTerm)
|
||||
registerTermConstructor(SymBool, newLiteralTerm)
|
||||
registerTermConstructor(SymKwNil, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymString, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymInteger, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymFloat, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymFraction, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymBool, newLiteralTerm)
|
||||
scan.RegisterTermConstructor(scan.SymKwNil, newLiteralTerm)
|
||||
}
|
||||
|
||||
+19
-16
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-selector-case.go
|
||||
@@ -7,19 +7,22 @@ package expr
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- selector case term
|
||||
|
||||
type selectorCase struct {
|
||||
filterList *term
|
||||
caseExpr Expr
|
||||
filterList *scan.Term
|
||||
caseExpr kern.Expr
|
||||
}
|
||||
|
||||
func (sc *selectorCase) String() string {
|
||||
var sb strings.Builder
|
||||
if sc.filterList != nil {
|
||||
sc.filterList.toString(&sb)
|
||||
sc.filterList.ToString(&sb)
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
sb.WriteByte('{')
|
||||
@@ -28,23 +31,23 @@ func (sc *selectorCase) String() string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
|
||||
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalSelectorCase,
|
||||
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
|
||||
tk := scan.NewValueToken(row, col, scan.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalSelectorCase,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval selector case
|
||||
func evalSelectorCase(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var ok bool
|
||||
if v, ok = opTerm.value().(*selectorCase); !ok {
|
||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
|
||||
if v, ok = opTerm.Value().(*selectorCase); !ok {
|
||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+21
-16
@@ -1,31 +1,36 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-var.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- variable term
|
||||
func newVarTerm(tk *Token) *term {
|
||||
t := &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalVar,
|
||||
func newVarTerm(tk *scan.Token) *scan.Term {
|
||||
t := &scan.Term{
|
||||
Tk: *tk,
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalVar,
|
||||
}
|
||||
t.tk.Sym = SymVariable
|
||||
t.Tk.Sym = scan.SymVariable
|
||||
return t
|
||||
}
|
||||
|
||||
// -------- eval func
|
||||
func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalVar(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var exists bool
|
||||
name := opTerm.source()
|
||||
if v, exists = GetVar(ctx, name); !exists {
|
||||
if info, exists := GetFuncInfo(ctx, name); exists {
|
||||
name := opTerm.Source()
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
v = info.Functor()
|
||||
} else {
|
||||
err = fmt.Errorf("undefined variable or function %q", name)
|
||||
@@ -36,5 +41,5 @@ func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymIdentifier, newVarTerm)
|
||||
scan.RegisterTermConstructor(scan.SymIdentifier, newVarTerm)
|
||||
}
|
||||
|
||||
+157
-91
@@ -1,34 +1,60 @@
|
||||
// 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 evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v, err = generalEvalAssign(ctx, opTerm, false)
|
||||
return
|
||||
}
|
||||
|
||||
func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAssign,
|
||||
EvalFunc: evalDeepCopyAssign,
|
||||
}
|
||||
}
|
||||
|
||||
func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v, err = generalEvalAssign(ctx, opTerm, true)
|
||||
return
|
||||
}
|
||||
|
||||
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,60 +63,99 @@ 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, deepCopy bool) (err error) {
|
||||
if leftTerm.Symbol() == scan.SymIndex {
|
||||
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
||||
} else {
|
||||
ctx.UnsafeSetVar(leftTerm.source(), v)
|
||||
if deepCopy {
|
||||
v = kern.Clone(v)
|
||||
}
|
||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
|
||||
var ok bool
|
||||
var dictAny, dotKey any
|
||||
var dict *kern.DictType
|
||||
|
||||
if err = dotTerm.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)
|
||||
dotLeftTerm := dotTerm.GetChild(0)
|
||||
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if dict, ok = dictAny.(*kern.DictType); !ok {
|
||||
err = dotTerm.Tk.ErrorExpectedGot(kern.DictTypeName)
|
||||
return
|
||||
}
|
||||
|
||||
rightChild := opTerm.children[1]
|
||||
dotRightTerm := dotTerm.Children[1]
|
||||
dotRightSym := dotRightTerm.Symbol()
|
||||
if dotRightSym == scan.SymVariable || dotRightSym == scan.SymString {
|
||||
dotKey = util.UnquoteString(dotRightTerm.Source())
|
||||
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v, err = rightChild.compute(ctx); err == nil {
|
||||
if functor, ok := v.(Functor); ok {
|
||||
if leftSym == SymVariable {
|
||||
if v, err = valueTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dict.SetItem(dotKey, v)
|
||||
return
|
||||
}
|
||||
|
||||
func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.Children[0]
|
||||
leftSym := leftTerm.Symbol()
|
||||
if leftSym == scan.SymDot {
|
||||
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
|
||||
} else 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]
|
||||
|
||||
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)
|
||||
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||
}
|
||||
} else {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -101,29 +166,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 +197,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,58 +211,58 @@ 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)
|
||||
err = assignValue(ctx, leftTerm, v, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,15 +271,16 @@ 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.SymColonEqual, newDeepCopyAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm)
|
||||
}
|
||||
|
||||
+54
-49
@@ -1,50 +1,55 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-bitwise.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- Bitwise NOT term
|
||||
|
||||
func newBitwiseNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBitwiseNot,
|
||||
evalFunc: evalBitwiseNot,
|
||||
func newBitwiseNotTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriBitwiseNot,
|
||||
EvalFunc: evalBitwiseNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalBitwiseNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var value any
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if value, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
if kern.IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise AND term
|
||||
|
||||
func newBitwiseAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseAnd,
|
||||
evalFunc: evalBitwiseAnd,
|
||||
func newBitwiseAndTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseAnd,
|
||||
EvalFunc: evalBitwiseAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
func bitwiseAnd(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
@@ -54,15 +59,15 @@ func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalBitwiseAnd(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
@@ -71,17 +76,17 @@ func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- Bitwise OR term
|
||||
|
||||
func newBitwiseOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseOr,
|
||||
func newBitwiseOrTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseOr,
|
||||
EvalFunc: evalBitwiseOr,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
func bitwiseOr(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
@@ -91,15 +96,15 @@ func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalBitwiseOr(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,17 +114,17 @@ func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- Bitwise XOR term
|
||||
|
||||
func newBitwiseXorTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseXor,
|
||||
func newBitwiseXorTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriBitwiseXor,
|
||||
EvalFunc: evalBitwiseXor,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
func bitwiseXor(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
@@ -129,15 +134,15 @@ func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if lok && rok {
|
||||
v = leftInt ^ rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalBitwiseXor(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -147,8 +152,8 @@ func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
||||
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
||||
scan.RegisterTermConstructor(scan.SymTilde, newBitwiseNotTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAmpersand, newBitwiseAndTerm)
|
||||
scan.RegisterTermConstructor(scan.SymVertBar, newBitwiseOrTerm)
|
||||
scan.RegisterTermConstructor(scan.SymCaret, newBitwiseXorTerm)
|
||||
}
|
||||
|
||||
+67
-61
@@ -1,52 +1,57 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-bool.go
|
||||
package expr
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- NOT term
|
||||
|
||||
func newNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priNot,
|
||||
evalFunc: evalNot,
|
||||
func newNotTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 1),
|
||||
Position: scan.PosPrefix,
|
||||
Priority: scan.PriNot,
|
||||
EvalFunc: evalNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var rightValue any
|
||||
|
||||
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
if rightValue, err = opTerm.EvalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if b, ok := ToBool(rightValue); ok {
|
||||
if b, ok := kern.ToBool(rightValue); ok {
|
||||
v = !b
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(rightValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- AND term
|
||||
|
||||
func newAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAnd,
|
||||
evalFunc: evalAnd,
|
||||
func newAndTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAnd,
|
||||
EvalFunc: evalAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
|
||||
func evalAnd(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
||||
v, err = evalAndWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalAndWithoutShortcut(ctx, self)
|
||||
@@ -54,47 +59,48 @@ func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
func evalAndWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftBool, rightBool bool
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftBool, lok = ToBool(leftValue)
|
||||
rightBool, rok = ToBool(rightValue)
|
||||
leftBool, lok = kern.ToBool(leftValue)
|
||||
rightBool, rok = kern.ToBool(rightValue)
|
||||
|
||||
if lok && rok {
|
||||
v = leftBool && rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
func evalAndWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if err = self.checkOperands(); err != nil {
|
||||
if err = self.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||
if leftValue, err = self.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftBool, lok := ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", TypeName(leftValue))
|
||||
return
|
||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
||||
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
|
||||
// return
|
||||
err = self.ErrIncompatibleType(leftValue, "left")
|
||||
} else if !leftBool {
|
||||
v = false
|
||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||
if rightBool, rok := ToBool(rightValue); rok {
|
||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
||||
v = rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -102,18 +108,18 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
|
||||
//-------- OR term
|
||||
|
||||
func newOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priOr,
|
||||
evalFunc: evalOr,
|
||||
func newOrTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriOr,
|
||||
EvalFunc: evalOr,
|
||||
}
|
||||
}
|
||||
|
||||
func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
if CtrlIsEnabled(ctx, ControlBoolShortcut) {
|
||||
func evalOr(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
||||
v, err = evalOrWithShortcut(ctx, self)
|
||||
} else {
|
||||
v, err = evalOrWithoutShortcut(ctx, self)
|
||||
@@ -121,47 +127,47 @@ func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
func evalOrWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftBool, rightBool bool
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
if leftValue, rightValue, err = self.EvalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftBool, lok = ToBool(leftValue)
|
||||
rightBool, rok = ToBool(rightValue)
|
||||
leftBool, lok = kern.ToBool(leftValue)
|
||||
rightBool, rok = kern.ToBool(rightValue)
|
||||
|
||||
if lok && rok {
|
||||
v = leftBool || rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if err = self.checkOperands(); err != nil {
|
||||
if err = self.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||
if leftValue, err = self.Children[0].Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftBool, lok := ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", TypeName(leftValue))
|
||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
|
||||
return
|
||||
} else if leftBool {
|
||||
v = true
|
||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||
if rightBool, rok := ToBool(rightValue); rok {
|
||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
||||
v = rightBool
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -169,7 +175,7 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymNot, newNotTerm)
|
||||
registerTermConstructor(SymAnd, newAndTerm)
|
||||
registerTermConstructor(SymOr, newOrTerm)
|
||||
scan.RegisterTermConstructor(scan.SymNot, newNotTerm)
|
||||
scan.RegisterTermConstructor(scan.SymAnd, newAndTerm)
|
||||
scan.RegisterTermConstructor(scan.SymOr, newOrTerm)
|
||||
}
|
||||
|
||||
+23
-18
@@ -1,50 +1,55 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// 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
@@ -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
@@ -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)
|
||||
}
|
||||
+39
-30
@@ -1,60 +1,69 @@
|
||||
// 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 {
|
||||
v = formatter.ToDict()
|
||||
} else if formatter, ok := sourceCtx.(Formatter); ok {
|
||||
v = formatter.ToString(0)
|
||||
} else {
|
||||
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
||||
keys := sourceCtx.EnumVars(nil)
|
||||
d := make(map[string]any)
|
||||
for _, key := range keys {
|
||||
d[key], _ = sourceCtx.GetVar(key)
|
||||
v = sourceCtx.ToDict()
|
||||
} else if childValue != nil {
|
||||
it, ok := childValue.(kern.Iterator)
|
||||
if !ok {
|
||||
it, err = NewIterator(ctx, childValue, nil)
|
||||
}
|
||||
if err == nil {
|
||||
var item any
|
||||
values := kern.NewLinkedListA()
|
||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||
values.PushBack(item)
|
||||
}
|
||||
keys = sourceCtx.EnumFuncs(func(name string) bool { return true })
|
||||
for _, key := range keys {
|
||||
d[key], _ = sourceCtx.GetFuncInfo(key)
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
v = values
|
||||
}
|
||||
v = d
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleDollar, newContextTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleDollar, newContextTerm)
|
||||
}
|
||||
|
||||
+16
-11
@@ -1,27 +1,32 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-ctrl.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- export all term
|
||||
|
||||
func newExportAllTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalExportAll,
|
||||
func newExportAllTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalExportAll,
|
||||
}
|
||||
}
|
||||
|
||||
func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
func evalExportAll(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
CtrlEnable(ctx, kern.ControlExportAll)
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleAt, newExportAllTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleAt, newExportAllTerm)
|
||||
}
|
||||
|
||||
+55
-50
@@ -1,38 +1,43 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-default.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
//-------- default term
|
||||
|
||||
func newDefaultTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalDefault,
|
||||
func newDefaultTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
EvalFunc: evalDefault,
|
||||
}
|
||||
}
|
||||
|
||||
func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var rightValue any
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
|
||||
v = leftValue
|
||||
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
v = rightValue
|
||||
}
|
||||
return
|
||||
@@ -40,32 +45,32 @@ func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- alternate term
|
||||
|
||||
func newAlternateTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalAlternate,
|
||||
func newAlternateTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
EvalFunc: evalAlternate,
|
||||
}
|
||||
}
|
||||
|
||||
func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalAlternate(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var rightValue any
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
|
||||
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists && leftValue != nil {
|
||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
v = rightValue
|
||||
}
|
||||
} else {
|
||||
@@ -76,41 +81,41 @@ func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
//-------- default assign term
|
||||
|
||||
func newDefaultAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priDefault,
|
||||
evalFunc: evalAssignDefault,
|
||||
func newDefaultAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriDefault,
|
||||
EvalFunc: evalAssignDefault,
|
||||
}
|
||||
}
|
||||
|
||||
func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var rightValue any
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
if leftTerm.tk.Sym != SymVariable {
|
||||
leftTerm := opTerm.Children[0]
|
||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
|
||||
v = leftValue
|
||||
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
if functor, ok := rightValue.(Functor); ok {
|
||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
if functor, ok := rightValue.(kern.Functor); ok {
|
||||
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
||||
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
||||
ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfDefault|kern.PfRepeat),
|
||||
})
|
||||
} else {
|
||||
v = rightValue
|
||||
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
||||
ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -118,7 +123,7 @@ func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
|
||||
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
|
||||
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDoubleQuestion, newDefaultTerm)
|
||||
scan.RegisterTermConstructor(scan.SymQuestionEqual, newDefaultAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymQuestionExclam, newAlternateTerm)
|
||||
}
|
||||
|
||||
+23
-20
@@ -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-digest.go
|
||||
@@ -7,51 +7,54 @@ 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 *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priIterOp,
|
||||
evalFunc: evalDigest,
|
||||
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 ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var it Iterator
|
||||
var it kern.Iterator
|
||||
var item, lastValue 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
|
||||
}
|
||||
|
||||
if it, err = NewIterator(leftValue); err != nil {
|
||||
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", TypeName(leftValue))
|
||||
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
|
||||
return nil, fmt.Errorf("left operand of 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("_index", it.Index())
|
||||
ctx.SetVar("_count", it.Count())
|
||||
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
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("_count")
|
||||
ctx.DeleteVar("_index")
|
||||
ctx.DeleteVar("#")
|
||||
ctx.DeleteVar("__")
|
||||
ctx.DeleteVar("_")
|
||||
if err != nil {
|
||||
break
|
||||
@@ -66,5 +69,5 @@ func evalDigest(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymKwDigest, newDigestTerm)
|
||||
scan.RegisterTermConstructor(scan.SymKwDigest, newDigestTerm)
|
||||
}
|
||||
|
||||
+70
-19
@@ -1,36 +1,42 @@
|
||||
// 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"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
)
|
||||
|
||||
// -------- 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.Symbol() == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
||||
opName := indexTerm.Source()
|
||||
if unboxedValue.HasOperation(opName) {
|
||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||
} else {
|
||||
@@ -38,17 +44,62 @@ 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].Symbol()
|
||||
// if s == scan.SymVariable || s == scan.SymString {
|
||||
// src := opTerm.Children[1].Source()
|
||||
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
// src = src[1 : len(src)-1]
|
||||
// }
|
||||
// if v, ok = unboxedValue.GetItem(src); !ok {
|
||||
// err = opTerm.Errorf("key %q not found", src)
|
||||
// }
|
||||
// } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
// if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
||||
// err = opTerm.Errorf("key %q not found", rightValue)
|
||||
// }
|
||||
// }
|
||||
v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1])
|
||||
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
|
||||
}
|
||||
|
||||
func dotGetDictItemValue(ctx kern.ExprContext, d *kern.DictType, rightTerm *scan.Term) (v any, err error) {
|
||||
var ok bool
|
||||
var rightValue any
|
||||
s := rightTerm.Symbol()
|
||||
if s == scan.SymVariable || s == scan.SymString {
|
||||
// src := rightTerm.Source()
|
||||
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
// src = src[1 : len(src)-1]
|
||||
// }
|
||||
src := util.UnquoteString(rightTerm.Source())
|
||||
if v, ok = d.GetItem(src); !ok {
|
||||
err = errDictKeyNotFound(rightTerm, src)
|
||||
}
|
||||
} else if rightValue, err = rightTerm.Compute(ctx); err == nil {
|
||||
if v, ok = d.GetItem(rightValue); !ok {
|
||||
err = errDictKeyNotFound(rightTerm, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func errDictKeyNotFound(term *scan.Term, key any) error {
|
||||
if s, ok := key.(string); ok {
|
||||
return term.Errorf("key %q not found", s)
|
||||
}
|
||||
return term.Errorf("key %v not found", key)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDot, newDotTerm)
|
||||
scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user