Compare commits
3 Commits
main
...
hook-train
| Author | SHA1 | Date | |
|---|---|---|---|
| ba1d887a05 | |||
| faff5a7e2c | |||
| 36f6846a3f |
@ -144,4 +144,4 @@ Variables and functions can be added to a context both programmatically and ad a
|
|||||||
|
|
||||||
== Expressions syntax
|
== Expressions syntax
|
||||||
|
|
||||||
See https://cdn.paas.portale-stac.it/howto/go-pkg/expr/doc/Expr.html[Expr documentation] for a complete reference of the expression syntax and available functions.
|
See #TODO link to doc/Expr.html#
|
||||||
|
|||||||
109
ast.go
109
ast.go
@ -7,10 +7,14 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Expr interface {
|
||||||
|
Eval(ctx ExprContext) (result any, err error)
|
||||||
|
eval(ctx ExprContext, preset bool) (result any, err error)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
//-------- ast
|
//-------- ast
|
||||||
|
|
||||||
type ast struct {
|
type ast struct {
|
||||||
@ -22,71 +26,66 @@ func NewAst() *ast {
|
|||||||
return &ast{}
|
return &ast{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) TypeName() string {
|
func (self *ast) ToForest() {
|
||||||
return "Expression"
|
if self.root != nil {
|
||||||
|
if self.forest == nil {
|
||||||
|
self.forest = make([]*term, 0)
|
||||||
}
|
}
|
||||||
|
self.forest = append(self.forest, self.root)
|
||||||
func (ast *ast) ToForest() {
|
self.root = nil
|
||||||
if ast.root != nil {
|
|
||||||
if ast.forest == nil {
|
|
||||||
ast.forest = make([]*term, 0)
|
|
||||||
}
|
|
||||||
ast.forest = append(ast.forest, ast.root)
|
|
||||||
ast.root = nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) String() string {
|
func (self *ast) String() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if ast.root == nil {
|
if self.root == nil {
|
||||||
sb.WriteString("(nil)")
|
sb.WriteString("(nil)")
|
||||||
} else {
|
} else {
|
||||||
ast.root.toString(&sb)
|
self.root.toString(&sb)
|
||||||
}
|
}
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) addTokens(tokens ...*Token) (err error) {
|
func (self *ast) addTokens(tokens ...*Token) (err error) {
|
||||||
for _, tk := range tokens {
|
for _, tk := range tokens {
|
||||||
if _, err = ast.addToken(tk); err != nil {
|
if err = self.addToken(tk); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (expr *ast) addToken(tk *Token) (err error) {
|
func (self *ast) addToken(tk *Token) (err error) {
|
||||||
// _, err = expr.addToken2(tk)
|
_, err = self.addToken2(tk)
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
func (ast *ast) addToken(tk *Token) (t *term, err error) {
|
func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
||||||
if t = newTerm(tk); t != nil {
|
if t = newTerm(tk, nil); t != nil {
|
||||||
err = ast.addTerm(t)
|
err = self.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("unexpected token %q", tk.String())
|
err = tk.Errorf("No term constructor for token %q", tk.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) addTerm(node *term) (err error) {
|
func (self *ast) addTerm(node *term) (err error) {
|
||||||
if ast.root == nil {
|
if self.root == nil {
|
||||||
ast.root = node
|
self.root = node
|
||||||
} else {
|
} else {
|
||||||
ast.root, err = ast.insert(ast.root, node)
|
self.root, err = self.insert(self.root, node)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) insert(tree, node *term) (root *term, err error) {
|
func (self *ast) insert(tree, node *term) (root *term, err error) {
|
||||||
if tree.getPriority() < node.getPriority() {
|
if tree.getPriority() < node.getPriority() {
|
||||||
root = tree
|
root = tree
|
||||||
if tree.isComplete() {
|
if tree.isComplete() {
|
||||||
var subRoot *term
|
var subRoot *term
|
||||||
last := tree.removeLastChild()
|
last := tree.removeLastChild()
|
||||||
if subRoot, err = ast.insert(last, node); err == nil {
|
subRoot, err = self.insert(last, node)
|
||||||
subRoot.setParent(tree)
|
subRoot.setParent(tree)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
node.setParent(tree)
|
node.setParent(tree)
|
||||||
}
|
}
|
||||||
@ -99,32 +98,28 @@ func (ast *ast) insert(tree, node *term) (root *term, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) Finish() {
|
func (self *ast) Finish() {
|
||||||
if ast.root == nil && ast.forest != nil && len(ast.forest) >= 1 {
|
if self.root == nil && self.forest != nil && len(self.forest) >= 1 {
|
||||||
ast.root = ast.forest[len(ast.forest)-1]
|
self.root = self.forest[len(self.forest)-1]
|
||||||
ast.forest = ast.forest[0 : len(ast.forest)-1]
|
self.forest = self.forest[0 : len(self.forest)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ast *ast) Eval(ctx kern.ExprContext) (result any, err error) {
|
func (self *ast) Eval(ctx ExprContext) (result any, err error) {
|
||||||
defer func() {
|
return self.eval(ctx, true)
|
||||||
if r := recover(); r != nil {
|
|
||||||
if errVal, ok := r.(error); ok {
|
|
||||||
err = errVal
|
|
||||||
} else {
|
|
||||||
err = errors.New("unexpected error while evaluating the expression")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
|
||||||
|
self.Finish()
|
||||||
|
|
||||||
|
if self.root != nil {
|
||||||
|
if preset {
|
||||||
|
initDefaultVars(ctx)
|
||||||
}
|
}
|
||||||
}()
|
if self.forest != nil {
|
||||||
|
for _, root := range self.forest {
|
||||||
ast.Finish()
|
if result, err = root.compute(ctx); err == nil {
|
||||||
|
ctx.setVar(ControlLastResult, result)
|
||||||
if ast.root != nil {
|
|
||||||
// initDefaultVars(ctx)
|
|
||||||
if ast.forest != nil {
|
|
||||||
for _, tree := range ast.forest {
|
|
||||||
if result, err = tree.Compute(ctx); err == nil {
|
|
||||||
ctx.UnsafeSetVar(kern.ControlLastResult, result)
|
|
||||||
} else {
|
} else {
|
||||||
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
|
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
|
||||||
break
|
break
|
||||||
@ -132,12 +127,10 @@ func (ast *ast) Eval(ctx kern.ExprContext) (result any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if result, err = ast.root.Compute(ctx); err == nil {
|
result, err = self.root.compute(ctx)
|
||||||
ctx.UnsafeSetVar(kern.ControlLastResult, result)
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
// } else {
|
err = errors.New("empty expression")
|
||||||
// err = errors.New("empty expression")
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// t_ast_test.go
|
// ast_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -44,10 +44,10 @@ func TestAddTokensBad(t *testing.T) {
|
|||||||
func TestAddUnknownTokens(t *testing.T) {
|
func TestAddUnknownTokens(t *testing.T) {
|
||||||
tk0 := NewToken(0, 0, SymPercent, "%")
|
tk0 := NewToken(0, 0, SymPercent, "%")
|
||||||
|
|
||||||
wantErr := errors.New(`unexpected token "%"`)
|
wantErr := errors.New(`No term constructor for token "%"`)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if _, gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
||||||
t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr)
|
t.Errorf("err: got <%v>, want <%v>", gotErr, wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,63 +0,0 @@
|
|||||||
// Copyright (c) 2024 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 {
|
|
||||||
kern.BaseFunctor
|
|
||||||
params []kern.ExprFuncParam
|
|
||||||
expr Expr
|
|
||||||
defCtx kern.ExprContext
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *exprFunctor) GetParams() (params []kern.ExprFuncParam) {
|
|
||||||
return functor.params
|
|
||||||
}
|
|
||||||
|
|
||||||
func newExprFunctor(e Expr, params []kern.ExprFuncParam, ctx kern.ExprContext) *exprFunctor {
|
|
||||||
var defCtx kern.ExprContext
|
|
||||||
if ctx != nil {
|
|
||||||
defCtx = ctx
|
|
||||||
}
|
|
||||||
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *exprFunctor) TypeName() string {
|
|
||||||
return "ExprFunctor"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *exprFunctor) GetDefinitionContext() kern.ExprContext {
|
|
||||||
return functor.defCtx
|
|
||||||
}
|
|
||||||
|
|
||||||
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.(kern.Functor); ok {
|
|
||||||
paramSpecs := funcArg.GetParams()
|
|
||||||
ctx.RegisterFunc(p.Name(), funcArg, kern.TypeAny, paramSpecs)
|
|
||||||
} else {
|
|
||||||
ctx.UnsafeSetVar(p.Name(), arg)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if missing == nil {
|
|
||||||
missing = make([]string, 0, 1)
|
|
||||||
}
|
|
||||||
missing = append(missing, p.Name())
|
|
||||||
// ctx.UnsafeSetVar(p.Name(), nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if missing != nil {
|
|
||||||
err = kern.ErrMissingParams(name, missing)
|
|
||||||
} else {
|
|
||||||
result, err = functor.expr.Eval(ctx)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
318
builtin-base.go
318
builtin-base.go
@ -1,318 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-base.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ParamDenominator = "denominator"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
result = args[kern.ParamValue] == nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
result = kern.IsFloat(args[kern.ParamValue])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
result = kern.IsString(args[kern.ParamValue])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
result = kern.IsRational(args[kern.ParamValue])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
result = kern.IsDict(args[kern.ParamValue])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *kern.FractionType:
|
|
||||||
result = v.N() != 0
|
|
||||||
case float64:
|
|
||||||
result = v != 0.0
|
|
||||||
case bool:
|
|
||||||
result = v
|
|
||||||
case string:
|
|
||||||
result = len(v) > 0
|
|
||||||
case *kern.ListType:
|
|
||||||
result = len(*v) > 0
|
|
||||||
case *kern.DictType:
|
|
||||||
result = len(*v) > 0
|
|
||||||
default:
|
|
||||||
err = kern.ErrCantConvert(name, v, "bool")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
|
||||||
result = int64(math.Trunc(v))
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
result = int64(1)
|
|
||||||
} else {
|
|
||||||
result = int64(0)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var i int
|
|
||||||
if i, err = strconv.Atoi(v); err == nil {
|
|
||||||
result = int64(i)
|
|
||||||
}
|
|
||||||
case *kern.FractionType:
|
|
||||||
result = int64(v.N() / v.D())
|
|
||||||
default:
|
|
||||||
err = kern.ErrCantConvert(name, v, "int")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
|
||||||
result = v
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
result = float64(1)
|
|
||||||
} else {
|
|
||||||
result = float64(0)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var f float64
|
|
||||||
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
|
||||||
result = f
|
|
||||||
}
|
|
||||||
case *kern.FractionType:
|
|
||||||
result = v.ToFloat()
|
|
||||||
default:
|
|
||||||
err = kern.ErrCantConvert(name, v, "float")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
|
||||||
result = strconv.FormatFloat(v, 'g', -1, 64)
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
result = "true"
|
|
||||||
} else {
|
|
||||||
result = "false"
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
result = v
|
|
||||||
case *kern.FractionType:
|
|
||||||
result = v.ToString(0)
|
|
||||||
case kern.Formatter:
|
|
||||||
result = v.ToString(0)
|
|
||||||
case fmt.Stringer:
|
|
||||||
result = v.String()
|
|
||||||
default:
|
|
||||||
err = kern.ErrCantConvert(name, v, "string")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = kern.ErrExpectedGot(name, "integer", args[ParamDenominator])
|
|
||||||
} else if den == 0 {
|
|
||||||
err = kern.ErrFuncDivisionByZero(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
result = kern.NewFraction(v, den)
|
|
||||||
}
|
|
||||||
case float64:
|
|
||||||
result, err = kern.Float64ToFraction(v)
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
result = kern.NewFraction(1, 1)
|
|
||||||
} else {
|
|
||||||
result = kern.NewFraction(0, 1)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
result, err = kern.MakeGeneratingFraction(v)
|
|
||||||
case *kern.FractionType:
|
|
||||||
result = v
|
|
||||||
default:
|
|
||||||
err = kern.ErrCantConvert(name, v, "float")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func iteratorFunc(ctx expr.ExprContext, name string, args []any) (result any, err error) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
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 Expr
|
|
||||||
|
|
||||||
parser := NewParser()
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = NewSimpleStoreWithoutGlobalContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
r := strings.NewReader(source)
|
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
|
||||||
|
|
||||||
if ast, err = parser.Parse(scanner); err == nil {
|
|
||||||
CtrlEnable(ctx, kern.ControlExportAll)
|
|
||||||
result, err = ast.Eval(ctx)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamName].(string); !ok {
|
|
||||||
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
|
||||||
}
|
|
||||||
|
|
||||||
if result, ok = args[kern.ParamValue]; ok && result != nil {
|
|
||||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
|
||||||
// } else {
|
|
||||||
// err = expr.ErrWrongParamType(name, expr.ParamSource, expr.TypeString, args[expr.ParamSource])
|
|
||||||
// }
|
|
||||||
} else if result, ok = ctx.GetVar(varName); !ok {
|
|
||||||
err = kern.ErrUnknownVar(name, varName)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamName].(string); !ok {
|
|
||||||
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
|
||||||
}
|
|
||||||
|
|
||||||
if result, ok = args[kern.ParamValue]; ok {
|
|
||||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
|
||||||
} else {
|
|
||||||
err = kern.ErrWrongParamType(name, kern.ParamValue, kern.TypeAny, args[kern.ParamValue])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
// var varName string
|
|
||||||
// var ok bool
|
|
||||||
|
|
||||||
// if varName, ok = args[kern.ParamName].(string); !ok {
|
|
||||||
// return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
|
||||||
// } else {
|
|
||||||
// ctx.GetParent().DeleteVar(varName)
|
|
||||||
// result = nil
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
//// import
|
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
|
||||||
anyParams := []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamValue),
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.RegisterFunc("isNil", kern.NewGolangFunctor(isNilFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isInt", kern.NewGolangFunctor(isIntFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isFloat", kern.NewGolangFunctor(isFloatFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isBool", kern.NewGolangFunctor(isBoolFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
|
|
||||||
|
|
||||||
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
|
|
||||||
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
|
|
||||||
ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams)
|
|
||||||
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
|
|
||||||
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamValue),
|
|
||||||
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("eval", kern.NewGolangFunctor(evalFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("var", kern.NewGolangFunctor(varFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamName),
|
|
||||||
NewFuncParamFlagDef(kern.ParamValue, PfDefault, nil),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("set", kern.NewGolangFunctor(setFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamName),
|
|
||||||
NewFuncParam(kern.ParamValue),
|
|
||||||
})
|
|
||||||
|
|
||||||
// ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
// NewFuncParam(kern.ParamName),
|
|
||||||
// NewFuncParam(kern.ParamValue),
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("base", ImportBuiltinsFuncs, "Base expression tools like isNil(), int(), etc.")
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-fmt.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getStdout(ctx kern.ExprContext) io.Writer {
|
|
||||||
var w io.Writer
|
|
||||||
if wany, exists := ctx.GetVar(kern.ControlStdout); exists && wany != nil {
|
|
||||||
w, _ = wany.(io.Writer)
|
|
||||||
}
|
|
||||||
if w == nil {
|
|
||||||
w = os.Stdout
|
|
||||||
}
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func printFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var n int = 0
|
|
||||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
|
||||||
argv := v.([]any)
|
|
||||||
n, err = fmt.Fprint(getStdout(ctx), argv...)
|
|
||||||
}
|
|
||||||
result = int64(n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func printLnFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var n int = 0
|
|
||||||
if v, exists := args[kern.ParamItem]; exists && v != nil {
|
|
||||||
argv := v.([]any)
|
|
||||||
n, err = fmt.Fprintln(getStdout(ctx), argv...)
|
|
||||||
} else {
|
|
||||||
n, err = fmt.Fprintln(getStdout(ctx))
|
|
||||||
}
|
|
||||||
result = int64(n)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportFmtFuncs(ctx kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("print", kern.NewGolangFunctor(printFunc), kern.TypeInt, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlag(kern.ParamItem, PfRepeat),
|
|
||||||
})
|
|
||||||
ctx.RegisterFunc("println", kern.NewGolangFunctor(printLnFunc), kern.TypeInt, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlag(kern.ParamItem, PfRepeat),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("fmt", ImportFmtFuncs, "String and console formatting functions")
|
|
||||||
}
|
|
||||||
@ -1,80 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-import.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
return importGeneral(ctx, name, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 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, int(it.Index())); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
var file *os.File
|
|
||||||
if file, err = os.Open(sourceFilepath); err == nil {
|
|
||||||
defer file.Close()
|
|
||||||
var expr *ast
|
|
||||||
scanner := NewScanner(file, DefaultTranslations())
|
|
||||||
parser := NewParser()
|
|
||||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
|
||||||
result, err = expr.Eval(ctx)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
result = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportImportFuncs(ctx kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("import", kern.NewGolangFunctor(importFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlag(kern.ParamFilepath, PfRepeat),
|
|
||||||
})
|
|
||||||
ctx.RegisterFunc("importAll", kern.NewGolangFunctor(importAllFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlag(kern.ParamFilepath, PfRepeat),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("import", ImportImportFuncs, "Functions import() and include()")
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-iterator.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
iterParamOperator = "operator"
|
|
||||||
iterParamVars = "vars"
|
|
||||||
iterVarStatus = "status"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
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].(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 *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
|
|
||||||
}
|
|
||||||
|
|
||||||
if vars != nil {
|
|
||||||
for key, value := range *vars {
|
|
||||||
var varName string
|
|
||||||
if varName, ok = key.(string); ok {
|
|
||||||
localCtx.UnsafeSetVar(varName, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var it kern.Iterator
|
|
||||||
var ok bool
|
|
||||||
var op kern.Functor
|
|
||||||
var v any
|
|
||||||
// var usingDefaultOp = false
|
|
||||||
var params map[string]any
|
|
||||||
var item any
|
|
||||||
|
|
||||||
localCtx := ctx.Clone()
|
|
||||||
localCtx.UnsafeSetVar(iterVarStatus, nil)
|
|
||||||
|
|
||||||
if it, op, err = parseRunArgs(localCtx, args); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
if op != nil {
|
|
||||||
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
|
|
||||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
var success bool
|
|
||||||
if success, ok = kern.ToBool(v); !success || !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if op == nil {
|
|
||||||
ctx.UnsafeSetVar(iterVarStatus, it.Count())
|
|
||||||
}
|
|
||||||
result, _ = localCtx.GetVar(iterVarStatus)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportIterFuncs(ctx kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("run", kern.NewGolangFunctor(runFunc), kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamIterator),
|
|
||||||
NewFuncParamFlag(iterParamOperator, PfOptional),
|
|
||||||
NewFuncParamFlag(iterParamVars, PfOptional),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
|
|
||||||
}
|
|
||||||
@ -1,185 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-math-arith.go
|
|
||||||
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 !(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 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 *kern.FractionType
|
|
||||||
var v any
|
|
||||||
|
|
||||||
level++
|
|
||||||
|
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
|
||||||
if list, ok := v.(*kern.ListType); ok {
|
|
||||||
v = NewListIterator(list, nil)
|
|
||||||
}
|
|
||||||
if subIter, ok := v.(kern.Iterator); ok {
|
|
||||||
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
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, int(it.Index())); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
|
|
||||||
if !sumAsFloat {
|
|
||||||
if kern.IsFloat(v) {
|
|
||||||
sumAsFloat = true
|
|
||||||
if sumAsFract {
|
|
||||||
floatSum = fractSum.ToFloat()
|
|
||||||
} else {
|
|
||||||
floatSum = float64(intSum)
|
|
||||||
}
|
|
||||||
} else if !sumAsFract && kern.IsFraction(v) {
|
|
||||||
fractSum = kern.NewFraction(intSum, 1)
|
|
||||||
sumAsFract = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sumAsFloat {
|
|
||||||
floatSum += kern.NumAsFloat(v)
|
|
||||||
} else if sumAsFract {
|
|
||||||
var item *kern.FractionType
|
|
||||||
var ok bool
|
|
||||||
if item, ok = v.(*kern.FractionType); !ok {
|
|
||||||
iv, _ := v.(int64)
|
|
||||||
item = kern.NewFraction(iv, 1)
|
|
||||||
}
|
|
||||||
fractSum = kern.SumFract(fractSum, item)
|
|
||||||
} else {
|
|
||||||
iv, _ := v.(int64)
|
|
||||||
intSum += iv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil || err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
if sumAsFloat {
|
|
||||||
result = floatSum
|
|
||||||
} else if sumAsFract {
|
|
||||||
result = fractSum
|
|
||||||
} else {
|
|
||||||
result = intSum
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 *kern.FractionType
|
|
||||||
var v any
|
|
||||||
|
|
||||||
level++
|
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
|
||||||
if list, ok := v.(*kern.ListType); ok {
|
|
||||||
v = NewListIterator(list, nil)
|
|
||||||
}
|
|
||||||
if subIter, ok := v.(kern.Iterator); ok {
|
|
||||||
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
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, int(it.Index())); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
|
|
||||||
if !mulAsFloat {
|
|
||||||
if kern.IsFloat(v) {
|
|
||||||
mulAsFloat = true
|
|
||||||
if mulAsFract {
|
|
||||||
floatProd = fractProd.ToFloat()
|
|
||||||
} else {
|
|
||||||
floatProd = float64(intProd)
|
|
||||||
}
|
|
||||||
} else if !mulAsFract && kern.IsFraction(v) {
|
|
||||||
fractProd = kern.NewFraction(intProd, 1)
|
|
||||||
mulAsFract = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if mulAsFloat {
|
|
||||||
floatProd *= kern.NumAsFloat(v)
|
|
||||||
} else if mulAsFract {
|
|
||||||
var item *kern.FractionType
|
|
||||||
var ok bool
|
|
||||||
if item, ok = v.(*kern.FractionType); !ok {
|
|
||||||
iv, _ := v.(int64)
|
|
||||||
item = kern.NewFraction(iv, 1)
|
|
||||||
}
|
|
||||||
fractProd = kern.MulFract(fractProd, item)
|
|
||||||
} else {
|
|
||||||
iv, _ := v.(int64)
|
|
||||||
intProd *= iv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil || err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
if mulAsFloat {
|
|
||||||
result = floatProd
|
|
||||||
} else if mulAsFract {
|
|
||||||
result = fractProd
|
|
||||||
} else {
|
|
||||||
result = intProd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("add", kern.NewGolangFunctor(addFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, int64(0)),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("mul", kern.NewGolangFunctor(mulFunc), kern.TypeNumber, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, int64(1)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("math.arith", ImportMathFuncs, "Functions add() and mul()")
|
|
||||||
}
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-os-file.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
const paramHandleOrPath = "handle-or-path"
|
|
||||||
const fileReadTextIteratorType = "fileReadTextIterator"
|
|
||||||
|
|
||||||
type fileReadTextIterator struct {
|
|
||||||
osReader *osReader
|
|
||||||
index int64
|
|
||||||
count int64
|
|
||||||
line string
|
|
||||||
autoClose bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
|
|
||||||
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) TypeName() string {
|
|
||||||
return fileReadTextIteratorType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) String() string {
|
|
||||||
if it.osReader != nil && it.osReader.fh != nil {
|
|
||||||
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Count() int64 {
|
|
||||||
return it.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
|
||||||
if it.osReader.fh != nil {
|
|
||||||
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
|
|
||||||
it.index++
|
|
||||||
it.count++
|
|
||||||
item = it.line[0 : len(it.line)-1]
|
|
||||||
} else if it.autoClose {
|
|
||||||
it.Clean()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Current() (item any, err error) {
|
|
||||||
if len(it.line) > 0 {
|
|
||||||
item = it.line[0 : len(it.line)-1]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Index() int64 {
|
|
||||||
return it.index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Reset() (err error) {
|
|
||||||
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
|
|
||||||
it.index = -1
|
|
||||||
it.count = 0
|
|
||||||
it.line = ""
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) HasOperation(name string) bool {
|
|
||||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Clean() (err error) {
|
|
||||||
if it.osReader.fh != nil {
|
|
||||||
if err = it.osReader.fh.Close(); err == nil {
|
|
||||||
it.osReader = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
|
||||||
switch name {
|
|
||||||
case kern.NextName:
|
|
||||||
v, err = it.Next()
|
|
||||||
case kern.ResetName:
|
|
||||||
err = it.Reset()
|
|
||||||
case kern.CleanName:
|
|
||||||
err = it.Clean()
|
|
||||||
case kern.IndexName:
|
|
||||||
v = int64(it.Index())
|
|
||||||
case kern.CurrentName:
|
|
||||||
v, err = it.Current()
|
|
||||||
case kern.CountName:
|
|
||||||
v = it.count
|
|
||||||
default:
|
|
||||||
err = kern.ErrNoOperation(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var handle *osReader
|
|
||||||
var invalidFileHandle any
|
|
||||||
var ok, autoClose bool
|
|
||||||
|
|
||||||
result = nil
|
|
||||||
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
|
|
||||||
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
|
|
||||||
var handleAny any
|
|
||||||
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if handleAny != nil {
|
|
||||||
handle = handleAny.(*osReader)
|
|
||||||
autoClose = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invalidFileHandle = args[paramHandleOrPath]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
|
||||||
result = newReadTextIterator(handle, autoClose)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func ImportOsIterFuncs(ctx ExprContext) {
|
|
||||||
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
|
||||||
// NewFuncParam(paramHandleOrPath),
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func init() {
|
|
||||||
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
|
|
||||||
// }
|
|
||||||
@ -1,263 +0,0 @@
|
|||||||
// Copyright (c) 2024 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/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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func errInvalidFileHandle(funcName string, v any) error {
|
|
||||||
if v != nil {
|
|
||||||
return fmt.Errorf("%s(): invalid file handle %v [%T]", funcName, v, v)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("%s(): invalid file handle", funcName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.Create(filePath); err == nil {
|
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errMissingFilePath(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.Open(filePath); err == nil {
|
|
||||||
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errMissingFilePath(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
|
||||||
var fh *os.File
|
|
||||||
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errMissingFilePath(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var handle osHandle
|
|
||||||
var invalidFileHandle any
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !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 err == nil {
|
|
||||||
err = fh.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, handle)
|
|
||||||
}
|
|
||||||
result = err == nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var handle osHandle
|
|
||||||
var invalidFileHandle any
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
|
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
|
||||||
if w, ok := handle.(*osWriter); ok {
|
|
||||||
if v, exists := args[kern.ParamItem]; exists {
|
|
||||||
argv := v.([]any)
|
|
||||||
result, err = fmt.Fprint(w.writer, argv...)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invalidFileHandle = handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var handle osHandle
|
|
||||||
var invalidFileHandle any
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
result = nil
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
|
||||||
if r, ok := handle.(*osReader); 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)
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if len(v) > 0 {
|
|
||||||
if v[len(v)-1] == limit {
|
|
||||||
result = v[0 : len(v)-1]
|
|
||||||
} else {
|
|
||||||
result = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
invalidFileHandle = handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
var handle osHandle
|
|
||||||
var invalidFileHandle any
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
result = nil
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
|
||||||
}
|
|
||||||
|
|
||||||
if handle != nil {
|
|
||||||
if r, ok := handle.(*osReader); ok {
|
|
||||||
var b []byte
|
|
||||||
b, err = io.ReadAll(r.reader)
|
|
||||||
result = string(b)
|
|
||||||
} else {
|
|
||||||
invalidFileHandle = handle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportOsFuncs(ctx kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("fileOpen", kern.NewGolangFunctor(openFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamFilepath),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileAppend", kern.NewGolangFunctor(appendFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamFilepath),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileCreate", kern.NewGolangFunctor(createFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamFilepath),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileClose", kern.NewGolangFunctor(closeFileFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamHandle),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileWriteText", kern.NewGolangFunctor(fileWriteTextFunc), kern.TypeInt, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamHandle),
|
|
||||||
NewFuncParamFlagDef(kern.ParamItem, PfDefault|PfRepeat, ""),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadText", kern.NewGolangFunctor(fileReadTextFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamHandle),
|
|
||||||
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadTextAll", kern.NewGolangFunctor(fileReadTextAllFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamHandle),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(paramHandleOrPath),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("os.file", ImportOsFuncs, "Operating system file functions")
|
|
||||||
}
|
|
||||||
@ -1,273 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtin-string.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
strParamOther = "other"
|
|
||||||
)
|
|
||||||
|
|
||||||
// --- Start of function definitions
|
|
||||||
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() {
|
|
||||||
if it.Index() > 0 {
|
|
||||||
sb.WriteString(sep)
|
|
||||||
}
|
|
||||||
if s, ok := v.(string); ok {
|
|
||||||
sb.WriteString(s)
|
|
||||||
} else {
|
|
||||||
err = kern.ErrExpectedGot(funcName, kern.TypeString, v)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
result = sb.String()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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].(*kern.ListType); ok {
|
|
||||||
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
|
||||||
} 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 = kern.ErrInvalidParameterValue(name, kern.ParamItem, v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = kern.ErrWrongParamType(name, kern.ParamSeparator, kern.TypeString, args[kern.ParamSeparator])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamSource].(string); !ok {
|
|
||||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
|
|
||||||
if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if start < 0 {
|
|
||||||
start = len(source) + start
|
|
||||||
}
|
|
||||||
|
|
||||||
if count < 0 {
|
|
||||||
count = len(source) - start
|
|
||||||
}
|
|
||||||
end := min(start+count, len(source))
|
|
||||||
result = source[start:end]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamSource].(string); !ok {
|
|
||||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
result = strings.TrimSpace(source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamSource].(string); !ok {
|
|
||||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else if v, exists := args[strParamOther]; exists {
|
|
||||||
argv := v.([]any)
|
|
||||||
for i, targetSpec := range argv {
|
|
||||||
if target, ok := targetSpec.(string); ok {
|
|
||||||
if strings.HasPrefix(source, target) {
|
|
||||||
result = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamSource].(string); !ok {
|
|
||||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else if v, exists := args[strParamOther]; exists {
|
|
||||||
argv := v.([]any)
|
|
||||||
for i, targetSpec := range argv {
|
|
||||||
if target, ok := targetSpec.(string); ok {
|
|
||||||
if strings.HasSuffix(source, target) {
|
|
||||||
result = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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[kern.ParamSource].(string); !ok {
|
|
||||||
return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
|
|
||||||
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[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)", kern.TypeName(args[kern.ParamCount]), args[kern.ParamCount])
|
|
||||||
}
|
|
||||||
|
|
||||||
if count > 0 {
|
|
||||||
parts = strings.SplitN(source, sep, count)
|
|
||||||
} else if count < 0 {
|
|
||||||
parts = strings.Split(source, sep)
|
|
||||||
} else {
|
|
||||||
parts = []string{}
|
|
||||||
}
|
|
||||||
list := make(kern.ListType, len(parts))
|
|
||||||
for i, part := range parts {
|
|
||||||
list[i] = part
|
|
||||||
}
|
|
||||||
result = &list
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- End of function definitions
|
|
||||||
|
|
||||||
// Import above functions in the context
|
|
||||||
func ImportStringFuncs(ctx kern.ExprContext) {
|
|
||||||
ctx.RegisterFunc("strJoin", kern.NewGolangFunctor(joinStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSeparator),
|
|
||||||
NewFuncParamFlag(kern.ParamItem, PfRepeat),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strSub", kern.NewGolangFunctor(subStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
NewFuncParamFlagDef(kern.ParamStart, PfDefault, int64(0)),
|
|
||||||
NewFuncParamFlagDef(kern.ParamCount, PfDefault, int64(-1)),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strSplit", kern.NewGolangFunctor(splitStrFunc), "list of "+kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
NewFuncParamFlagDef(kern.ParamSeparator, PfDefault, ""),
|
|
||||||
NewFuncParamFlagDef(kern.ParamCount, PfDefault, int64(-1)),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strTrim", kern.NewGolangFunctor(trimStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strStartsWith", kern.NewGolangFunctor(startsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
NewFuncParam(kern.ParamPrefix),
|
|
||||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strEndsWith", kern.NewGolangFunctor(endsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
NewFuncParam(kern.ParamSuffix),
|
|
||||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strUpper", kern.NewGolangFunctor(upperStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
})
|
|
||||||
|
|
||||||
ctx.RegisterFunc("strLower", kern.NewGolangFunctor(lowerStrFunc), kern.TypeString, []kern.ExprFuncParam{
|
|
||||||
NewFuncParam(kern.ParamSource),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register the import function in the import-register.
|
|
||||||
// That will allow to import all function of this module by the "builtin" operator."
|
|
||||||
func init() {
|
|
||||||
RegisterBuiltinModule("string", ImportStringFuncs, "string utilities")
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// builtins-register.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
type builtinModule struct {
|
|
||||||
importFunc func(kern.ExprContext)
|
|
||||||
description string
|
|
||||||
imported bool
|
|
||||||
}
|
|
||||||
|
|
||||||
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(kern.ExprContext), description string) {
|
|
||||||
if builtinModuleRegister == nil {
|
|
||||||
builtinModuleRegister = make(map[string]*builtinModule)
|
|
||||||
}
|
|
||||||
if _, exists := builtinModuleRegister[name]; exists {
|
|
||||||
panic(fmt.Errorf("module %q already registered", name))
|
|
||||||
}
|
|
||||||
builtinModuleRegister[name] = newBuiltinModule(importFunc, description)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IterateBuiltinModules(op func(name, description string, imported bool) bool) {
|
|
||||||
if op != nil {
|
|
||||||
for name, mod := range builtinModuleRegister {
|
|
||||||
if !op(name, mod.description, mod.imported) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----
|
|
||||||
func init() {
|
|
||||||
if builtinModuleRegister == nil {
|
|
||||||
builtinModuleRegister = make(map[string]*builtinModule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -18,17 +18,17 @@ func NewByteSlider(size int) *ByteSlider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slider *ByteSlider) PushEnd(b byte) {
|
func (self *ByteSlider) PushEnd(b byte) {
|
||||||
if slider.length == cap(slider.buf) {
|
if self.length == cap(self.buf) {
|
||||||
slider.length--
|
self.length--
|
||||||
for i := 0; i < slider.length; i++ {
|
for i := 0; i < self.length; i++ {
|
||||||
slider.buf[i] = slider.buf[i+1]
|
self.buf[i] = self.buf[i+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
slider.buf[slider.length] = b
|
self.buf[self.length] = b
|
||||||
slider.length++
|
self.length++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slider *ByteSlider) Equal(target []byte) bool {
|
func (self *ByteSlider) Equal(target []byte) bool {
|
||||||
return target != nil && bytes.Equal(slider.buf, target)
|
return target != nil && bytes.Equal(self.buf, target)
|
||||||
}
|
}
|
||||||
|
|||||||
42
context.go
Normal file
42
context.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// context.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// ---- Function template
|
||||||
|
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
|
||||||
|
|
||||||
|
// ---- Functor interface
|
||||||
|
type Functor interface {
|
||||||
|
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type simpleFunctor struct {
|
||||||
|
f FuncTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *simpleFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
return functor.f(ctx, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Function Info
|
||||||
|
type ExprFunc interface {
|
||||||
|
Name() string
|
||||||
|
MinArgs() int
|
||||||
|
MaxArgs() int
|
||||||
|
Functor() Functor
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----Expression Context
|
||||||
|
type ExprContext interface {
|
||||||
|
Clone() ExprContext
|
||||||
|
GetVar(varName string) (value any, exists bool)
|
||||||
|
SetVar(varName string, value any)
|
||||||
|
setVar(varName string, value any)
|
||||||
|
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||||
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
|
GetFuncInfo(name string) ExprFunc
|
||||||
|
Call(name string, args []any) (result any, err error)
|
||||||
|
RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
||||||
|
}
|
||||||
62
control.go
Normal file
62
control.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// control.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// Preset control variables
|
||||||
|
const (
|
||||||
|
ControlLastResult = "last"
|
||||||
|
ControlBoolShortcut = "_bool_shortcut"
|
||||||
|
ControlImportPath = "_import_path"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Other control variables
|
||||||
|
const (
|
||||||
|
control_export_all = "_export_all"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initial values
|
||||||
|
const (
|
||||||
|
init_import_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initDefaultVars(ctx ExprContext) {
|
||||||
|
ctx.SetVar(ControlBoolShortcut, true)
|
||||||
|
ctx.SetVar(ControlImportPath, init_import_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func enable(ctx ExprContext, name string) {
|
||||||
|
if strings.HasPrefix(name, "_") {
|
||||||
|
ctx.SetVar(name, true)
|
||||||
|
} else {
|
||||||
|
ctx.SetVar("_"+name, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disable(ctx ExprContext, name string) {
|
||||||
|
if strings.HasPrefix(name, "_") {
|
||||||
|
ctx.SetVar(name, false)
|
||||||
|
} else {
|
||||||
|
ctx.SetVar("_"+name, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEnabled(ctx ExprContext, name string) (status bool) {
|
||||||
|
if v, exists := ctx.GetVar(name); exists {
|
||||||
|
if b, ok := v.(bool); ok {
|
||||||
|
status = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getControlString(ctx ExprContext, name string) (s string, exists bool) {
|
||||||
|
var v any
|
||||||
|
if v, exists = ctx.GetVar(name); exists {
|
||||||
|
s, exists = v.(string)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
307
data-cursor.go
307
data-cursor.go
@ -1,307 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// data-cursors.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dataCursor struct {
|
|
||||||
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 int64
|
|
||||||
count int64
|
|
||||||
current any
|
|
||||||
lastErr error
|
|
||||||
resource any
|
|
||||||
nextFunc kern.Functor
|
|
||||||
cleanFunc kern.Functor
|
|
||||||
resetFunc kern.Functor
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDataCursor(ctx kern.ExprContext, ds map[string]kern.Functor, resource any) (dc *dataCursor) {
|
|
||||||
dc = &dataCursor{
|
|
||||||
ds: ds,
|
|
||||||
initState: true,
|
|
||||||
// cursorValid: true,
|
|
||||||
index: -1,
|
|
||||||
count: 0,
|
|
||||||
current: nil,
|
|
||||||
lastErr: nil,
|
|
||||||
resource: resource,
|
|
||||||
ctx: ctx.Clone(),
|
|
||||||
nextFunc: ds[kern.NextName],
|
|
||||||
cleanFunc: ds[kern.CleanName],
|
|
||||||
resetFunc: ds[kern.ResetName],
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Context() kern.ExprContext {
|
|
||||||
return dc.ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) TypeName() string {
|
|
||||||
return "DataCursor"
|
|
||||||
}
|
|
||||||
|
|
||||||
// func mapToString(m map[string]Functor) string {
|
|
||||||
// var sb strings.Builder
|
|
||||||
// sb.WriteByte('{')
|
|
||||||
// for key, _ := range m {
|
|
||||||
// if sb.Len() > 1 {
|
|
||||||
// sb.WriteString(fmt.Sprintf(", %q: func(){}", key))
|
|
||||||
// } else {
|
|
||||||
// sb.WriteString(fmt.Sprintf("%q: func(){}", key))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// sb.WriteByte('}')
|
|
||||||
// return sb.String()
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (dc *dataCursor) String() string {
|
|
||||||
return "$()"
|
|
||||||
/*
|
|
||||||
var sb strings.Builder
|
|
||||||
sb.WriteString(fmt.Sprintf(`$(
|
|
||||||
index: %d,
|
|
||||||
ds: %s,
|
|
||||||
ctx: `, dc.index, mapToString(dc.ds)))
|
|
||||||
CtxToBuilder(&sb, dc.ctx, 1)
|
|
||||||
sb.WriteByte(')')
|
|
||||||
return sb.String()
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
|
||||||
exists = slices.Contains([]string{kern.CleanName, kern.ResetName, kern.CurrentName, kern.IndexName}, name)
|
|
||||||
if !exists {
|
|
||||||
f, ok := dc.ds[name]
|
|
||||||
exists = ok && kern.IsFunctor(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
|
|
||||||
if name == kern.IndexName {
|
|
||||||
value = int64(dc.Index())
|
|
||||||
} else if name == kern.CleanName {
|
|
||||||
err = dc.Clean()
|
|
||||||
} else if name == kern.ResetName {
|
|
||||||
err = dc.Reset()
|
|
||||||
} else if functor, ok := dc.ds[name]; ok && kern.IsFunctor(functor) {
|
|
||||||
ctx := kern.CloneContext(dc.ctx)
|
|
||||||
value, err = functor.InvokeNamed(ctx, name, args)
|
|
||||||
kern.ExportObjects(dc.ctx, ctx)
|
|
||||||
} else {
|
|
||||||
err = kern.ErrNoOperation(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (dc *dataCursor) Reset() (err error) {
|
|
||||||
// if dc.resetFunc != nil {
|
|
||||||
// if dc.resource != nil {
|
|
||||||
// ctx := cloneContext(dc.ctx)
|
|
||||||
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
|
||||||
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
|
||||||
// exportObjects(dc.ctx, ctx)
|
|
||||||
// dc.index = -1
|
|
||||||
// dc.count = 0
|
|
||||||
// dc.initState = true
|
|
||||||
// dc.current = nil
|
|
||||||
// dc.lastErr = nil
|
|
||||||
// } else {
|
|
||||||
// err = errInvalidDataSource()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// err = errNoOperation(ResetName)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (dc *dataCursor) Reset() (err error) {
|
|
||||||
if dc.resetFunc != nil {
|
|
||||||
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
|
|
||||||
dc.initState = true
|
|
||||||
dc.current = nil
|
|
||||||
dc.lastErr = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Clean() (err error) {
|
|
||||||
if dc.cleanFunc != nil {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (dc *dataCursor) Clean() (err error) {
|
|
||||||
// if dc.cleanFunc != nil {
|
|
||||||
// if dc.resource != nil {
|
|
||||||
// ctx := cloneContext(dc.ctx)
|
|
||||||
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
|
||||||
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
|
||||||
// exportObjects(dc.ctx, ctx)
|
|
||||||
// } else {
|
|
||||||
// err = errInvalidDataSource()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// err = errNoOperation(CleanName)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
|
||||||
dc.init()
|
|
||||||
|
|
||||||
if dc.current != nil {
|
|
||||||
item = dc.current
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) checkFilter(filter kern.Functor, item any) (accepted bool, err error) {
|
|
||||||
var v any
|
|
||||||
var ok bool
|
|
||||||
ctx := kern.CloneContext(dc.ctx)
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) init() {
|
|
||||||
if dc.initState {
|
|
||||||
dc.initState = false
|
|
||||||
dc.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
|
|
||||||
if dc.initState {
|
|
||||||
dc.init()
|
|
||||||
} else if err = dc.lastErr; err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
current = dc.current
|
|
||||||
filter := dc.ds[kern.FilterName]
|
|
||||||
mapper := dc.ds[kern.MapName]
|
|
||||||
var item any
|
|
||||||
for item == nil && dc.lastErr == nil {
|
|
||||||
ctx := kern.CloneContext(dc.ctx)
|
|
||||||
dc.index++
|
|
||||||
|
|
||||||
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 {
|
|
||||||
accepted := true
|
|
||||||
if filter != nil {
|
|
||||||
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
|
||||||
item = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if accepted {
|
|
||||||
dc.count++
|
|
||||||
}
|
|
||||||
if item != nil && mapper != nil {
|
|
||||||
item, dc.lastErr = dc.mapItem(mapper, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kern.ExportObjects(dc.ctx, ctx)
|
|
||||||
}
|
|
||||||
dc.current = item
|
|
||||||
if dc.lastErr != nil {
|
|
||||||
dc.index--
|
|
||||||
dc.Clean()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
|
|
||||||
// if dc.initState {
|
|
||||||
// dc.init()
|
|
||||||
// } else if err = dc.lastErr; err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// current = dc.current
|
|
||||||
// if dc.resource != nil {
|
|
||||||
// filter := dc.ds[FilterName]
|
|
||||||
// mapper := dc.ds[MapName]
|
|
||||||
// var item any
|
|
||||||
// for item == nil && dc.lastErr == nil {
|
|
||||||
// ctx := 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 {
|
|
||||||
// if item == nil {
|
|
||||||
// dc.lastErr = io.EOF
|
|
||||||
// } else {
|
|
||||||
// accepted := true
|
|
||||||
// if filter != nil {
|
|
||||||
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
|
||||||
// item = nil
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if accepted {
|
|
||||||
// dc.count++
|
|
||||||
// }
|
|
||||||
// if item != nil && mapper != nil {
|
|
||||||
// item, dc.lastErr = dc.mapItem(mapper, item)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// exportObjects(dc.ctx, ctx)
|
|
||||||
// }
|
|
||||||
// dc.current = item
|
|
||||||
// if dc.lastErr != nil {
|
|
||||||
// dc.index--
|
|
||||||
// dc.Clean()
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// dc.lastErr = errInvalidDataSource()
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (dc *dataCursor) Index() int64 {
|
|
||||||
return dc.index - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Count() int64 {
|
|
||||||
return dc.count
|
|
||||||
}
|
|
||||||
223
dict-iterator.go
223
dict-iterator.go
@ -1,223 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// dict-iterator.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
type dictIterMode int
|
|
||||||
|
|
||||||
const (
|
|
||||||
dictIterModeKeys dictIterMode = iota
|
|
||||||
dictIterModeValues
|
|
||||||
dictIterModeItems
|
|
||||||
)
|
|
||||||
|
|
||||||
type DictIterator struct {
|
|
||||||
a *kern.DictType
|
|
||||||
count int64
|
|
||||||
index int64
|
|
||||||
keys []any
|
|
||||||
iterMode dictIterMode
|
|
||||||
}
|
|
||||||
|
|
||||||
type sortType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
sortTypeNone sortType = iota
|
|
||||||
sortTypeAsc
|
|
||||||
sortTypeDesc
|
|
||||||
sortTypeDefault = sortTypeAsc
|
|
||||||
)
|
|
||||||
|
|
||||||
func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
|
|
||||||
it.keys = make([]any, 0, len(m))
|
|
||||||
|
|
||||||
if sort == sortTypeNone {
|
|
||||||
for keyAny := range m {
|
|
||||||
it.keys = append(it.keys, keyAny)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scalarMap := make(map[string]any, len(m))
|
|
||||||
scalerKeys := make([]string, 0, len(m))
|
|
||||||
for keyAny := range m {
|
|
||||||
keyStr := fmt.Sprint(keyAny)
|
|
||||||
scalarMap[keyStr] = keyAny
|
|
||||||
scalerKeys = append(scalerKeys, keyStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch sort {
|
|
||||||
case sortTypeAsc:
|
|
||||||
slices.Sort(scalerKeys)
|
|
||||||
case sortTypeDesc:
|
|
||||||
slices.Sort(scalerKeys)
|
|
||||||
slices.Reverse(scalerKeys)
|
|
||||||
}
|
|
||||||
for _, keyStr := range scalerKeys {
|
|
||||||
it.keys = append(it.keys, scalarMap[keyStr])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) {
|
|
||||||
var sortType = sortTypeNone
|
|
||||||
var s string
|
|
||||||
var argAny any
|
|
||||||
|
|
||||||
dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
|
|
||||||
if len(args) > 0 {
|
|
||||||
argAny = args[0]
|
|
||||||
} else {
|
|
||||||
argAny = "default"
|
|
||||||
}
|
|
||||||
if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
|
|
||||||
switch strings.ToLower(s) {
|
|
||||||
case "a", "asc":
|
|
||||||
sortType = sortTypeAsc
|
|
||||||
case "d", "desc":
|
|
||||||
sortType = sortTypeDesc
|
|
||||||
case "n", "none", "nosort", "no-sort":
|
|
||||||
sortType = sortTypeNone
|
|
||||||
case "", "default":
|
|
||||||
sortType = sortTypeDefault
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("invalid sort type %q", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
if len(args) > 1 {
|
|
||||||
argAny = args[1]
|
|
||||||
} else {
|
|
||||||
argAny = "default"
|
|
||||||
}
|
|
||||||
|
|
||||||
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
|
|
||||||
switch strings.ToLower(s) {
|
|
||||||
case "k", "key", "keys":
|
|
||||||
dictIt.iterMode = dictIterModeKeys
|
|
||||||
case "v", "value", "values":
|
|
||||||
dictIt.iterMode = dictIterModeValues
|
|
||||||
case "i", "item", "items":
|
|
||||||
dictIt.iterMode = dictIterModeItems
|
|
||||||
case "", "default":
|
|
||||||
dictIt.iterMode = dictIterModeKeys
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("invalid iteration mode %q", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dictIt.makeKeys(*dict, sortType)
|
|
||||||
return dictIt, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMapIterator(m map[any]any) (it *DictIterator) {
|
|
||||||
it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil}
|
|
||||||
it.makeKeys(m, sortTypeNone)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) String() string {
|
|
||||||
var l = int64(0)
|
|
||||||
if it.a != nil {
|
|
||||||
l = int64(len(*it.a))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("$({#%d})", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) TypeName() string {
|
|
||||||
return "DictIterator"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) HasOperation(name string) bool {
|
|
||||||
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
|
|
||||||
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, kern.KeyName, kern.ValueName}, name)
|
|
||||||
return yes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) 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
|
|
||||||
case kern.KeyName:
|
|
||||||
if it.index >= 0 && it.index < int64(len(it.keys)) {
|
|
||||||
v = it.keys[it.index]
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
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 = kern.ErrNoOperation(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Current() (item any, err error) {
|
|
||||||
if it.index >= 0 && it.index < int64(len(it.keys)) {
|
|
||||||
switch it.iterMode {
|
|
||||||
case dictIterModeKeys:
|
|
||||||
item = it.keys[it.index]
|
|
||||||
case dictIterModeValues:
|
|
||||||
a := *(it.a)
|
|
||||||
item = a[it.keys[it.index]]
|
|
||||||
case dictIterModeItems:
|
|
||||||
a := *(it.a)
|
|
||||||
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
|
|
||||||
item = kern.NewList(pair)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Next() (item any, err error) {
|
|
||||||
it.index++
|
|
||||||
if item, err = it.Current(); err != io.EOF {
|
|
||||||
it.count++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Index() int64 {
|
|
||||||
return it.index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Count() int64 {
|
|
||||||
return it.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Reset() error {
|
|
||||||
it.index = -1
|
|
||||||
it.count = 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *DictIterator) Clean() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
1749
doc/Expr.adoc
1749
doc/Expr.adoc
File diff suppressed because it is too large
Load Diff
3617
doc/Expr.html
3617
doc/Expr.html
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
16
expr.go
16
expr.go
@ -1,16 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// expr.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
kern "git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ----Expression interface
|
|
||||||
type Expr interface {
|
|
||||||
kern.Typer
|
|
||||||
Eval(ctx kern.ExprContext) (result any, err error)
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
72
expr_test.go
Normal file
72
expr_test.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// expr_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestExpr(t *testing.T) {
|
||||||
|
type inputType struct {
|
||||||
|
source string
|
||||||
|
wantResult any
|
||||||
|
wantErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
succeeded := 0
|
||||||
|
failed := 0
|
||||||
|
|
||||||
|
inputs1 := []inputType{
|
||||||
|
{`f=openFile("/tmp/test2.txt"); line=readFile(f); closeFile(f); line`, "ciao", nil},
|
||||||
|
//{`f = func(op){op()}; f(func(){2})`, int64(2), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, input := range inputs1 {
|
||||||
|
var expr Expr
|
||||||
|
var gotResult any
|
||||||
|
var gotErr error
|
||||||
|
|
||||||
|
ctx := NewSimpleFuncStore()
|
||||||
|
// ImportMathFuncs(ctx)
|
||||||
|
// ImportImportFunc(ctx)
|
||||||
|
ImportOsFuncs(ctx)
|
||||||
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
|
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
|
r := strings.NewReader(input.source)
|
||||||
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
|
|
||||||
|
good := true
|
||||||
|
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
||||||
|
gotResult, gotErr = expr.Eval(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotResult != input.wantResult {
|
||||||
|
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotErr != input.wantErr {
|
||||||
|
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
||||||
|
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
|
||||||
|
good = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if good {
|
||||||
|
succeeded++
|
||||||
|
} else {
|
||||||
|
failed++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
||||||
|
}
|
||||||
142
func-import.go
Normal file
142
func-import.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// func-import.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ENV_EXPR_PATH = "EXPR_PATH"
|
||||||
|
|
||||||
|
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
return importGeneral(ctx, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func includeFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
enable(ctx, control_export_all)
|
||||||
|
return importGeneral(ctx, name, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var dirList []string
|
||||||
|
|
||||||
|
dirList = addEnvImportDirs(dirList)
|
||||||
|
dirList = addPresetImportDirs(ctx, dirList)
|
||||||
|
result, err = doImport(ctx, name, dirList, NewFlatArrayIterator(args))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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 %T, string expected", funcName, paramPos+1, paramValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func addEnvImportDirs(dirList []string) []string {
|
||||||
|
if dirSpec, exists := os.LookupEnv(ENV_EXPR_PATH); exists {
|
||||||
|
dirs := strings.Split(dirSpec, ":")
|
||||||
|
if dirList == nil {
|
||||||
|
dirList = dirs
|
||||||
|
} else {
|
||||||
|
dirList = append(dirList, dirs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirList
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPresetImportDirs(ctx ExprContext, dirList []string) []string {
|
||||||
|
if dirSpec, exists := getControlString(ctx, ControlImportPath); exists {
|
||||||
|
dirs := strings.Split(dirSpec, ":")
|
||||||
|
if dirList == nil {
|
||||||
|
dirList = dirs
|
||||||
|
} else {
|
||||||
|
dirList = append(dirList, dirs...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirList
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFile(filePath string) bool {
|
||||||
|
info, err := os.Stat(filePath)
|
||||||
|
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||||
|
for _, dir := range dirList {
|
||||||
|
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
||||||
|
filePath = fullPath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPathRelative(filePath string) bool {
|
||||||
|
unixPath := filepath.ToSlash(filePath)
|
||||||
|
return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../")
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
|
||||||
|
if path.IsAbs(filename) || isPathRelative(filename) {
|
||||||
|
if isFile(filename) {
|
||||||
|
filePath = filename
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filePath = searchAmongPath(filename, dirList)
|
||||||
|
}
|
||||||
|
if len(filePath) == 0 {
|
||||||
|
err = fmt.Errorf("source file %q not found", filename)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func doImport(ctx ExprContext, name string, dirList []string, it 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 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
var file *os.File
|
||||||
|
if file, err = os.Open(sourceFilepath); err == nil {
|
||||||
|
defer file.Close()
|
||||||
|
var expr *ast
|
||||||
|
scanner := NewScanner(file, DefaultTranslations())
|
||||||
|
parser := NewParser(ctx)
|
||||||
|
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
||||||
|
result, err = expr.eval(ctx, false)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
} else {
|
||||||
|
result = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportImportFuncs(ctx ExprContext) {
|
||||||
|
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
||||||
|
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
||||||
|
}
|
||||||
114
func-math.go
Normal file
114
func-math.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// funcs-math.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
||||||
|
if !(isNumber(paramValue) || isList(paramValue)) {
|
||||||
|
err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
||||||
|
var sumAsFloat = false
|
||||||
|
var floatSum float64 = 0.0
|
||||||
|
var intSum int64 = 0
|
||||||
|
var v any
|
||||||
|
|
||||||
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if array, ok := v.([]any); ok {
|
||||||
|
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sumAsFloat && isFloat(v) {
|
||||||
|
sumAsFloat = true
|
||||||
|
floatSum = float64(intSum)
|
||||||
|
}
|
||||||
|
if sumAsFloat {
|
||||||
|
floatSum += numAsFloat(v)
|
||||||
|
} else {
|
||||||
|
iv, _ := v.(int64)
|
||||||
|
intSum += iv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil || err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
if sumAsFloat {
|
||||||
|
result = floatSum
|
||||||
|
} else {
|
||||||
|
result = intSum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
result, err = doAdd(ctx, name, NewFlatArrayIterator(args))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
||||||
|
var mulAsFloat = false
|
||||||
|
var floatProd float64 = 1.0
|
||||||
|
var intProd int64 = 1
|
||||||
|
var v any
|
||||||
|
|
||||||
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if array, ok := v.([]any); ok {
|
||||||
|
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mulAsFloat && isFloat(v) {
|
||||||
|
mulAsFloat = true
|
||||||
|
floatProd = float64(intProd)
|
||||||
|
}
|
||||||
|
if mulAsFloat {
|
||||||
|
floatProd *= numAsFloat(v)
|
||||||
|
} else {
|
||||||
|
iv, _ := v.(int64)
|
||||||
|
intProd *= iv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil || err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
if mulAsFloat {
|
||||||
|
result = floatProd
|
||||||
|
} else {
|
||||||
|
result = intProd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
result, err = doMul(ctx, name, NewFlatArrayIterator(args))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportMathFuncs(ctx ExprContext) {
|
||||||
|
ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
|
||||||
|
ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerImport("math.arith", ImportMathFuncs)
|
||||||
|
}
|
||||||
164
func-os.go
Normal file
164
func-os.go
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// func-os.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type osHandle interface {
|
||||||
|
getFile() *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
type osWriter struct {
|
||||||
|
fh *os.File
|
||||||
|
writer *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *osWriter) getFile() *os.File {
|
||||||
|
return h.fh
|
||||||
|
}
|
||||||
|
|
||||||
|
type osReader struct {
|
||||||
|
fh *os.File
|
||||||
|
reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *osReader) getFile() *os.File {
|
||||||
|
return h.fh
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var filePath string
|
||||||
|
if len(args) > 0 {
|
||||||
|
filePath, _ = args[0].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filePath) > 0 {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Create(filePath); err == nil {
|
||||||
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%s(): missing the file path", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var filePath string
|
||||||
|
if len(args) > 0 {
|
||||||
|
filePath, _ = args[0].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(filePath) > 0 {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Open(filePath); err == nil {
|
||||||
|
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%s(): missing the file path", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var filePath string
|
||||||
|
if len(args) > 0 {
|
||||||
|
filePath, _ = args[0].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if 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)}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%s(): missing the file path", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var handle osHandle
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
handle, _ = args[0].(osHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle != nil {
|
||||||
|
if fh := handle.getFile(); fh != nil {
|
||||||
|
if w, ok := handle.(*osWriter); ok {
|
||||||
|
err = w.writer.Flush()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = fh.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%s(): invalid file handle", name)
|
||||||
|
}
|
||||||
|
result = err == nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var handle osHandle
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
handle, _ = args[0].(osHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle != nil {
|
||||||
|
if fh := handle.getFile(); fh != nil {
|
||||||
|
if w, ok := handle.(*osWriter); ok {
|
||||||
|
result, err = fmt.Fprint(w.writer, args[1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var handle osHandle
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
handle, _ = args[0].(osHandle)
|
||||||
|
}
|
||||||
|
|
||||||
|
if handle != nil {
|
||||||
|
if fh := handle.getFile(); fh != nil {
|
||||||
|
if r, ok := handle.(*osReader); ok {
|
||||||
|
var limit byte = '\n'
|
||||||
|
var v string
|
||||||
|
if len(args) > 1 {
|
||||||
|
if s, ok := args[1].(string); ok && len(s) > 0 {
|
||||||
|
limit = s[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v, err = r.reader.ReadString(limit); err == nil || err == io.EOF {
|
||||||
|
if len(v) > 0 && v[len(v)-1] == limit {
|
||||||
|
result = v[0 : len(v)-1]
|
||||||
|
} else {
|
||||||
|
result = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportOsFuncs(ctx ExprContext) {
|
||||||
|
ctx.RegisterFunc("openFile", &simpleFunctor{f: openFileFunc}, 1, 1)
|
||||||
|
ctx.RegisterFunc("appendFile", &simpleFunctor{f: appendFileFunc}, 1, 1)
|
||||||
|
ctx.RegisterFunc("createFile", &simpleFunctor{f: createFileFunc}, 1, 1)
|
||||||
|
ctx.RegisterFunc("writeFile", &simpleFunctor{f: writeFileFunc}, 1, -1)
|
||||||
|
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
||||||
|
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
||||||
|
}
|
||||||
47
function-register.go
Normal file
47
function-register.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// function-register.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var functionRegister map[string]func(ExprContext)
|
||||||
|
|
||||||
|
func registerImport(name string, importFunc func(ExprContext)) {
|
||||||
|
if functionRegister == nil {
|
||||||
|
functionRegister = make(map[string]func(ExprContext))
|
||||||
|
}
|
||||||
|
functionRegister[name] = importFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||||
|
var importFunc func(ExprContext)
|
||||||
|
if importFunc, exists = functionRegister[name]; exists {
|
||||||
|
importFunc(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||||
|
var matched bool
|
||||||
|
for name, importFunc := range functionRegister {
|
||||||
|
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||||
|
if matched {
|
||||||
|
count++
|
||||||
|
importFunc(ctx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if functionRegister == nil {
|
||||||
|
functionRegister = make(map[string]func(ExprContext))
|
||||||
|
}
|
||||||
|
}
|
||||||
387
function.go
387
function.go
@ -1,387 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// function.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ---- Function template
|
|
||||||
// type FuncTemplate func(ctx expr.ExprContext, name string, args map[string]any) (result any, err error)
|
|
||||||
|
|
||||||
// ---- Common functor definition
|
|
||||||
type BaseFunctor struct {
|
|
||||||
info kern.ExprFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *BaseFunctor) ToString(opt kern.FmtOpt) (s string) {
|
|
||||||
if functor.info != nil {
|
|
||||||
s = functor.info.ToString(opt)
|
|
||||||
} else {
|
|
||||||
s = "func(){}"
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *BaseFunctor) GetParams() (params []kern.ExprFuncParam) {
|
|
||||||
if functor.info != nil {
|
|
||||||
return functor.info.Params()
|
|
||||||
} else {
|
|
||||||
return []kern.ExprFuncParam{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *BaseFunctor) SetFunc(info kern.ExprFunc) {
|
|
||||||
functor.info = info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *BaseFunctor) GetFunc() kern.ExprFunc {
|
|
||||||
return functor.info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *BaseFunctor) GetDefinitionContext() kern.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) kern.ExprFuncParam {
|
|
||||||
return &funcParamInfo{name: name}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFuncParamFlag(name string, flags paramFlags) kern.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 kern.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 expr.ExprFunc
|
|
||||||
type funcInfo struct {
|
|
||||||
name string
|
|
||||||
minArgs int
|
|
||||||
maxArgs int
|
|
||||||
functor kern.Functor
|
|
||||||
formalParams []kern.ExprFuncParam
|
|
||||||
returnType string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newFuncInfo(name string, functor kern.Functor, returnType string, params []kern.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() []kern.ExprFuncParam {
|
|
||||||
return info.formalParams
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) ReturnType() string {
|
|
||||||
return info.returnType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) ToString(opt kern.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(kern.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() kern.Functor {
|
|
||||||
return info.functor
|
|
||||||
}
|
|
||||||
|
|
||||||
func (info *funcInfo) AllocContext(parentCtx kern.ExprContext) (ctx kern.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) kern.ExprFuncParam {
|
|
||||||
for _, spec := range info.formalParams {
|
|
||||||
if spec.Name() == paramName {
|
|
||||||
return spec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func initActualParams(ctx kern.ExprContext, info kern.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 := kern.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 = kern.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 = kern.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 = kern.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 expr.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 expr.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 expr.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
|
|
||||||
// }
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// global-context.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//var globalCtx *SimpleStore
|
|
||||||
|
|
||||||
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(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
|
||||||
if !strings.HasPrefix(name, "_") {
|
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
|
||||||
globalCtx.SetVar(name, newValue)
|
|
||||||
}
|
|
||||||
return currentValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
|
||||||
if !strings.HasPrefix(name, "_") {
|
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
|
||||||
}
|
|
||||||
return currentValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
|
||||||
if !strings.HasPrefix(name, "_") {
|
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
|
||||||
currentStatus, _ = v.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.SetVar(name, true)
|
|
||||||
return currentStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
|
||||||
if !strings.HasPrefix(name, "_") {
|
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
|
||||||
currentStatus, _ = v.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.SetVar(name, false)
|
|
||||||
return currentStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
// v, exists = globalCtx.GetVar(name)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 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 InitGlobal() (ctx kern.ExprContext) {
|
|
||||||
ctx = NewSimpleStoreWithoutGlobalContext()
|
|
||||||
kern.InitDefaultVars(ctx)
|
|
||||||
ImportBuiltinsFuncs(ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
6
go.mod
6
go.mod
@ -1,7 +1,3 @@
|
|||||||
module git.portale-stac.it/go-pkg/expr
|
module git.portale-stac.it/go-pkg/expr
|
||||||
|
|
||||||
go 1.22.0
|
go 1.21.6
|
||||||
|
|
||||||
toolchain go1.23.3
|
|
||||||
|
|
||||||
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -1,4 +0,0 @@
|
|||||||
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=
|
|
||||||
4
graph.go
4
graph.go
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
//go:build graph
|
|
||||||
|
|
||||||
// graph.go
|
// graph.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
@ -55,7 +53,7 @@ func (r *Reticle) computeCharWidth() {
|
|||||||
if v := ref.node.value(); v != nil {
|
if v := ref.node.value(); v != nil {
|
||||||
ref.label = fmt.Sprintf("%v", v)
|
ref.label = fmt.Sprintf("%v", v)
|
||||||
} else {
|
} else {
|
||||||
ref.label = ref.node.Source()
|
ref.label = ref.node.source()
|
||||||
}
|
}
|
||||||
r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets
|
r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
//go:build graph
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
// t_graph_test.go
|
// graph_test.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
49
helpers.go
49
helpers.go
@ -6,23 +6,18 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
|
func EvalString(ctx ExprContext, source string) (result any, err error) {
|
||||||
var tree *ast
|
var tree *ast
|
||||||
|
|
||||||
r := strings.NewReader(source)
|
r := strings.NewReader(source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
parser := NewParser()
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
if tree, err = parser.Parse(scanner); err == nil {
|
if tree, err = parser.Parse(scanner); err == nil {
|
||||||
result, err = tree.Eval(ctx)
|
result, err = tree.eval(ctx, true)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -37,21 +32,18 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func EvalStringV(source string, args []Arg) (result any, err error) {
|
func EvalStringV(source string, args []Arg) (result any, err error) {
|
||||||
ctx := NewSimpleStoreWithoutGlobalContext()
|
ctx := NewSimpleFuncStore()
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if util.IsFunc(arg.Value) {
|
if isFunc(arg.Value) {
|
||||||
if f, ok := arg.Value.(kern.FuncTemplate); ok {
|
if f, ok := arg.Value.(FuncTemplate); ok {
|
||||||
functor := kern.NewGolangFunctor(f)
|
functor := &simpleFunctor{f: f}
|
||||||
// ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
ctx.RegisterFunc(arg.Name, functor, 0, -1)
|
||||||
ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, 0),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
||||||
}
|
}
|
||||||
} else if integer, ok := kern.AnyInteger(arg.Value); ok {
|
} else if integer, ok := anyInteger(arg.Value); ok {
|
||||||
ctx.SetVar(arg.Name, integer)
|
ctx.SetVar(arg.Name, integer)
|
||||||
} else if float, ok := kern.AnyFloat(arg.Value); ok {
|
} else if float, ok := anyFloat(arg.Value); ok {
|
||||||
ctx.SetVar(arg.Name, float)
|
ctx.SetVar(arg.Name, float)
|
||||||
} else if _, ok := arg.Value.(string); ok {
|
} else if _, ok := arg.Value.(string); ok {
|
||||||
ctx.SetVar(arg.Name, arg.Value)
|
ctx.SetVar(arg.Name, arg.Value)
|
||||||
@ -67,24 +59,3 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
|
|
||||||
var tree *ast
|
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
|
||||||
parser := NewParser()
|
|
||||||
|
|
||||||
if tree, err = parser.Parse(scanner); err == nil {
|
|
||||||
result, err = tree.Eval(ctx)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
defer fh.Close()
|
|
||||||
result, err = EvalStream(ctx, fh)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
81
helpers_test.go
Normal file
81
helpers_test.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// helpers_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func subtract(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x, xok := args[0].(int64)
|
||||||
|
y, yok := args[1].(int64)
|
||||||
|
if xok && yok {
|
||||||
|
result = x - y
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalStringA(t *testing.T) {
|
||||||
|
|
||||||
|
source := `a + b * subtract(4,2)`
|
||||||
|
args := []Arg{
|
||||||
|
{"a", uint8(1)},
|
||||||
|
{"b", int8(2)},
|
||||||
|
{"subtract", FuncTemplate(subtract)},
|
||||||
|
// force coverage
|
||||||
|
{"a16", uint16(1)},
|
||||||
|
{"b16", int16(2)},
|
||||||
|
{"a32", uint32(1)},
|
||||||
|
{"b32", int32(2)},
|
||||||
|
{"a64", uint64(1)},
|
||||||
|
{"b64", int64(2)},
|
||||||
|
{"f32", float32(1.0)},
|
||||||
|
{"f64", float64(1.0)},
|
||||||
|
}
|
||||||
|
|
||||||
|
wantResult := int64(5)
|
||||||
|
gotResult, gotErr := EvalStringA(source, args...)
|
||||||
|
if value, ok := gotResult.(int64); ok && value != wantResult {
|
||||||
|
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||||
|
t.Errorf("Error: %v", gotErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEvalString(t *testing.T) {
|
||||||
|
|
||||||
|
ctx := NewSimpleVarStore()
|
||||||
|
ctx.SetVar("a", uint8(1))
|
||||||
|
ctx.SetVar("b", int8(2))
|
||||||
|
ctx.SetVar("f", 2.0)
|
||||||
|
// force coverage
|
||||||
|
ctx.SetVar("a16", uint16(1))
|
||||||
|
ctx.SetVar("b16", int16(2))
|
||||||
|
ctx.SetVar("a32", uint32(1))
|
||||||
|
ctx.SetVar("b32", int32(2))
|
||||||
|
ctx.SetVar("a64", uint64(1))
|
||||||
|
ctx.SetVar("b64", int64(2))
|
||||||
|
ctx.SetVar("f32", float32(1.0))
|
||||||
|
ctx.SetVar("f64", float64(1.0))
|
||||||
|
|
||||||
|
// force coverage
|
||||||
|
ctx.GetFuncInfo("dummy")
|
||||||
|
ctx.Call("dummy", []any{})
|
||||||
|
|
||||||
|
source := `a + b * f`
|
||||||
|
|
||||||
|
wantResult := float64(5)
|
||||||
|
gotResult, gotErr := EvalString(ctx, source)
|
||||||
|
if value, ok := gotResult.(float64); ok && value != wantResult {
|
||||||
|
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||||
|
t.Errorf("Error: %v", gotErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
hook_test.txt
Normal file
3
hook_test.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
First test
|
||||||
|
next commit
|
||||||
|
nr 3
|
||||||
127
import-utils.go
127
import-utils.go
@ -1,127 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// import-utils.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ENV_EXPR_SOURCE_PATH = "EXPR_PATH"
|
|
||||||
ENV_EXPR_PLUGIN_PATH = "EXPR_PLUGIN_PATH"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// func addSourceEnvImportDirs(varName string, dirList []string) []string {
|
|
||||||
// return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func addPluginEnvImportDirs(varName string, dirList []string) []string {
|
|
||||||
// return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func addEnvImportDirs(envVarName string, dirList []string) []string {
|
|
||||||
if dirSpec, exists := os.LookupEnv(envVarName); exists {
|
|
||||||
dirs := strings.Split(dirSpec, ":")
|
|
||||||
if dirList == nil {
|
|
||||||
dirList = dirs
|
|
||||||
} else {
|
|
||||||
dirList = append(dirList, dirs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dirList
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
} else {
|
|
||||||
if len(endingPath) > 0 {
|
|
||||||
for _, d := range dirs {
|
|
||||||
dirList = append(dirList, path.Join(d, endingPath))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
dirList = append(dirList, dirs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dirList
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildSearchDirList(ctx kern.ExprContext, endingPath, envVarName string) (dirList []string) {
|
|
||||||
dirList = addEnvImportDirs(envVarName, dirList)
|
|
||||||
dirList = addSearchDirs(ctx, endingPath, dirList)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func isFile(filePath string) bool {
|
|
||||||
info, err := os.Stat(filePath)
|
|
||||||
return (err == nil || errors.Is(err, os.ErrExist)) && info.Mode().IsRegular()
|
|
||||||
}
|
|
||||||
|
|
||||||
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
suffix := SHAREDLIBRARY_EXTENSION
|
|
||||||
if strings.HasSuffix(filename, ".debug") {
|
|
||||||
suffix += ".debug"
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dir := range dirList {
|
|
||||||
if dir, err = util.ExpandPath(dir); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
|
||||||
filePath = fullPath
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
subdir := strings.TrimSuffix(filename, suffix)
|
|
||||||
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
|
||||||
filePath = fullPath
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPathRelative(filePath string) bool {
|
|
||||||
unixPath := filepath.ToSlash(filePath)
|
|
||||||
return strings.HasPrefix(unixPath, "./") || strings.HasPrefix(unixPath, "../")
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
|
|
||||||
if filename, err = util.ExpandPath(filename); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if path.IsAbs(filename) || isPathRelative(filename) {
|
|
||||||
if isFile(filename) {
|
|
||||||
filePath = filename
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
filePath = searchAmongPath(filename, dirList)
|
|
||||||
}
|
|
||||||
if len(filePath) == 0 {
|
|
||||||
err = fmt.Errorf("file %q not found", filename)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
137
int-iterator.go
137
int-iterator.go
@ -1,137 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// int-iterator.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IntIterator struct {
|
|
||||||
count int64
|
|
||||||
index int64
|
|
||||||
start int64
|
|
||||||
stop int64
|
|
||||||
step int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
|
||||||
var argc int = 0
|
|
||||||
if args != nil {
|
|
||||||
argc = len(args)
|
|
||||||
}
|
|
||||||
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
|
|
||||||
if argc >= 1 {
|
|
||||||
if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if argc >= 2 {
|
|
||||||
it.start = it.stop
|
|
||||||
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if argc >= 3 {
|
|
||||||
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else if it.start > it.stop {
|
|
||||||
it.step = -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if it.step == 0 {
|
|
||||||
err = fmt.Errorf("step cannot be zero")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if it.start < it.stop && it.step < 0 {
|
|
||||||
err = fmt.Errorf("step cannot be negative when start < stop")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if it.start > it.stop && it.step > 0 {
|
|
||||||
err = fmt.Errorf("step cannot be positive when start > stop")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
it.Reset()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) String() string {
|
|
||||||
return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) TypeName() string {
|
|
||||||
return "IntIterator"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) HasOperation(name string) bool {
|
|
||||||
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
|
|
||||||
return yes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
|
||||||
switch name {
|
|
||||||
case kern.NextName:
|
|
||||||
v, err = it.Next()
|
|
||||||
case kern.ResetName:
|
|
||||||
err = it.Reset()
|
|
||||||
case kern.CleanName:
|
|
||||||
err = it.Clean()
|
|
||||||
case kern.IndexName:
|
|
||||||
v = int64(it.Index())
|
|
||||||
case kern.CurrentName:
|
|
||||||
v, err = it.Current()
|
|
||||||
case kern.CountName:
|
|
||||||
v = it.count
|
|
||||||
default:
|
|
||||||
err = kern.ErrNoOperation(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Current() (item any, err error) {
|
|
||||||
if it.start <= it.stop {
|
|
||||||
if it.index >= it.start && it.index < it.stop {
|
|
||||||
item = it.index
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if it.index > it.stop && it.index <= it.start {
|
|
||||||
item = it.index
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Next() (item any, err error) {
|
|
||||||
it.index += it.step
|
|
||||||
if item, err = it.Current(); err != io.EOF {
|
|
||||||
it.count++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Index() int64 {
|
|
||||||
return it.index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Count() int64 {
|
|
||||||
return it.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Reset() error {
|
|
||||||
it.index = it.start - it.step
|
|
||||||
it.count = 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IntIterator) Clean() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// iter-factory.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewIterator(value any) (it kern.Iterator, err error) {
|
|
||||||
if value == nil {
|
|
||||||
return NewArrayIterator([]any{}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := value.(type) {
|
|
||||||
case *kern.ListType:
|
|
||||||
it = NewListIterator(v, nil)
|
|
||||||
case *kern.DictType:
|
|
||||||
it, err = NewDictIterator(v, nil)
|
|
||||||
case []any:
|
|
||||||
it = NewArrayIterator(v)
|
|
||||||
case kern.Iterator:
|
|
||||||
it = v
|
|
||||||
default:
|
|
||||||
it = NewArrayIterator([]any{value})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
40
iterator.go
Normal file
40
iterator.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type Iterator interface {
|
||||||
|
Reset()
|
||||||
|
Next() (item any, err error) // must return io.EOF after the last item
|
||||||
|
Index() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlatArrayIterator struct {
|
||||||
|
a []any
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFlatArrayIterator(array []any) *FlatArrayIterator {
|
||||||
|
return &FlatArrayIterator{a: array, index: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Reset() {
|
||||||
|
it.index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Next() (item any, err error) {
|
||||||
|
if it.index < len(it.a) {
|
||||||
|
item = it.a[it.index]
|
||||||
|
it.index++
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Index() int {
|
||||||
|
return it.index - 1
|
||||||
|
}
|
||||||
@ -1,23 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// bind-go-function.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
// ---- Linking with Go functions
|
|
||||||
type golangFunctor struct {
|
|
||||||
BaseFunctor
|
|
||||||
f FuncTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGolangFunctor(f FuncTemplate) *golangFunctor {
|
|
||||||
return &golangFunctor{f: f}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *golangFunctor) TypeName() string {
|
|
||||||
return "GoFunctor"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
|
||||||
return functor.f(ctx, name, args)
|
|
||||||
}
|
|
||||||
27
kern/bool.go
27
kern/bool.go
@ -1,27 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// string.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
func IsBool(v any) (ok bool) {
|
|
||||||
_, ok = v.(bool)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToBool(v any) (b bool, ok bool) {
|
|
||||||
ok = true
|
|
||||||
switch x := v.(type) {
|
|
||||||
case string:
|
|
||||||
b = len(x) > 0
|
|
||||||
case float64:
|
|
||||||
b = x != 0.0
|
|
||||||
case int64:
|
|
||||||
b = x != 0
|
|
||||||
case bool:
|
|
||||||
b = x
|
|
||||||
default:
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// common-errors.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ErrMissingParams(funcName string, missing []string) (err error) {
|
|
||||||
return fmt.Errorf("%s(): missing params -- %s", funcName, strings.Join(missing, ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
|
|
||||||
if maxArgs < 0 {
|
|
||||||
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s(): too few params -- expected %d, got %d", funcName, minArgs, argCount)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
|
|
||||||
err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- General errors
|
|
||||||
|
|
||||||
func ErrCantConvert(funcName string, value any, kind string) error {
|
|
||||||
return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrExpectedGot(funcName string, kind string, value any) error {
|
|
||||||
return fmt.Errorf("%s(): expected %s, got %s (%#v)", funcName, kind, TypeName(value), value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrFuncDivisionByZero(funcName string) error {
|
|
||||||
return fmt.Errorf("%s(): division by zero", funcName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// func ErrDivisionByZero() error {
|
|
||||||
// return fmt.Errorf("division by zero")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// --- Parameter errors
|
|
||||||
|
|
||||||
// func ErrMissingRequiredParameter(funcName, paramName string) error {
|
|
||||||
// return fmt.Errorf("%s(): missing required parameter %q", funcName, paramName)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
|
|
||||||
return fmt.Errorf("%s(): invalid value %s (%#v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func undefArticle(s string) (article string) {
|
|
||||||
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
|
|
||||||
article = "an"
|
|
||||||
} else {
|
|
||||||
article = "a"
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func prependUndefArticle(s string) (result string) {
|
|
||||||
return undefArticle(s) + " " + s
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
|
||||||
var artWantType, artGotType string
|
|
||||||
gotType := TypeName(paramValue)
|
|
||||||
artGotType = prependUndefArticle(gotType)
|
|
||||||
artWantType = prependUndefArticle(paramType)
|
|
||||||
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrUnknownParam(funcName, paramName string) error {
|
|
||||||
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrUnknownVar(funcName, varName string) error {
|
|
||||||
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Operator errors
|
|
||||||
|
|
||||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
|
|
||||||
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.Source())
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// common-params.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
const (
|
|
||||||
ParamArgs = "args"
|
|
||||||
ParamCount = "count"
|
|
||||||
ParamItem = "item"
|
|
||||||
ParamIndex = "index"
|
|
||||||
ParamParts = "parts"
|
|
||||||
ParamSeparator = "separator"
|
|
||||||
ParamSource = "source"
|
|
||||||
ParamSuffix = "suffix"
|
|
||||||
ParamPrefix = "prefix"
|
|
||||||
ParamStart = "start"
|
|
||||||
ParamEnd = "end"
|
|
||||||
ParamValue = "value"
|
|
||||||
ParamName = "name"
|
|
||||||
ParamEllipsis = "..."
|
|
||||||
ParamFilepath = "filepath"
|
|
||||||
ParamDirpath = "dirpath"
|
|
||||||
ParamHandle = "handle"
|
|
||||||
ParamResource = "resource"
|
|
||||||
ParamIterator = "iterator"
|
|
||||||
)
|
|
||||||
|
|
||||||
// to be moved in its own source file
|
|
||||||
const (
|
|
||||||
ConstLastIndex = 0xFFFF_FFFF
|
|
||||||
)
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// common-type-names.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypeAny = "any"
|
|
||||||
TypeNil = "nil"
|
|
||||||
TypeBoolean = "boolean"
|
|
||||||
TypeFloat = "float"
|
|
||||||
TypeFraction = "fraction"
|
|
||||||
TypeFileHandle = "file-handle"
|
|
||||||
TypeInt = "integer"
|
|
||||||
TypeItem = "item"
|
|
||||||
TypeIterator = "iterator"
|
|
||||||
TypeNumber = "number"
|
|
||||||
TypePair = "pair"
|
|
||||||
TypeString = "string"
|
|
||||||
TypeDict = "dict"
|
|
||||||
TypeListOf = "list-of-"
|
|
||||||
TypeListOfStrings = "list-of-strings"
|
|
||||||
)
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
func Equal(value1, value2 any) (equal bool) {
|
|
||||||
if value1 == nil && value2 == nil {
|
|
||||||
equal = true
|
|
||||||
} else if value1 == nil || value2 == nil {
|
|
||||||
equal = false
|
|
||||||
} else if IsBool(value1) && IsBool(value2) {
|
|
||||||
equal = value1.(bool) == value2.(bool)
|
|
||||||
} else if IsList(value1) && IsList(value2) {
|
|
||||||
ls1 := value1.(*ListType)
|
|
||||||
ls2 := value2.(*ListType)
|
|
||||||
equal = ls1.Equals(*ls2)
|
|
||||||
} else if IsDict(value1) && IsDict(value2) {
|
|
||||||
d1 := value1.(*DictType)
|
|
||||||
d2 := value2.(*DictType)
|
|
||||||
equal = d1.Equals(*d2)
|
|
||||||
} else if IsInteger(value1) && IsInteger(value2) {
|
|
||||||
equal = value1.(int64) == value2.(int64)
|
|
||||||
} else if IsString(value1) && IsString(value2) {
|
|
||||||
equal = value1.(string) == value2.(string)
|
|
||||||
} else if IsFloat(value1) && IsFloat(value2) {
|
|
||||||
equal = value1.(float64) == value2.(float64)
|
|
||||||
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
|
|
||||||
if eq, err := CmpAnyFract(value1, value2); err == nil {
|
|
||||||
equal = eq == 0
|
|
||||||
}
|
|
||||||
} else if !reflect.DeepEqual(value1, value2) {
|
|
||||||
equal = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// context-helpers.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
func CloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
|
||||||
if sourceCtx != nil {
|
|
||||||
clonedCtx = sourceCtx.Clone()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportVar(ctx ExprContext, name string, value any) {
|
|
||||||
if name[0] == '@' {
|
|
||||||
name = name[1:]
|
|
||||||
}
|
|
||||||
ctx.UnsafeSetVar(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
|
||||||
if name[0] == '@' {
|
|
||||||
name = name[1:]
|
|
||||||
}
|
|
||||||
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
|
|
||||||
}
|
|
||||||
|
|
||||||
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] == '_') }) {
|
|
||||||
// fmt.Printf("\tExporting %q\n", refName)
|
|
||||||
refValue, _ := sourceCtx.GetVar(refName)
|
|
||||||
exportVar(destCtx, refName, refValue)
|
|
||||||
}
|
|
||||||
// Export functions
|
|
||||||
for _, refName := range sourceCtx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
|
||||||
if info, _ := sourceCtx.GetFuncInfo(refName); info != nil {
|
|
||||||
exportFunc(destCtx, refName, info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportObjectsToParent(sourceCtx ExprContext) {
|
|
||||||
if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
|
|
||||||
ExportObjects(parentCtx, sourceCtx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// control.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// Preset control variables
|
|
||||||
const (
|
|
||||||
ControlPreset = "_preset"
|
|
||||||
ControlLastResult = "last"
|
|
||||||
ControlBoolShortcut = "_bool_shortcut"
|
|
||||||
ControlSearchPath = "_search_path"
|
|
||||||
ControlParentContext = "_parent_context"
|
|
||||||
ControlStdout = "_stdout"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Other control variables
|
|
||||||
const (
|
|
||||||
ControlExportAll = "_export_all"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Initial values
|
|
||||||
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) {
|
|
||||||
if _, exists := ctx.GetVar(ControlPreset); exists {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ctx.UnsafeSetVar(ControlPreset, true)
|
|
||||||
ctx.UnsafeSetVar(ControlBoolShortcut, true)
|
|
||||||
ctx.UnsafeSetVar(ControlSearchPath, init_search_path)
|
|
||||||
}
|
|
||||||
@ -1,193 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// dict-type.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDict(dictAny map[any]any) (dict *DictType) {
|
|
||||||
var d DictType
|
|
||||||
if dictAny != nil {
|
|
||||||
d = make(DictType, len(dictAny))
|
|
||||||
for i, item := range dictAny {
|
|
||||||
d[i] = item
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d = make(DictType)
|
|
||||||
}
|
|
||||||
dict = &d
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDict(dictAny map[any]Term) (dict *DictType) {
|
|
||||||
// TODO Change with a call to NewDict()
|
|
||||||
var d DictType
|
|
||||||
if dictAny != nil {
|
|
||||||
d = make(DictType, len(dictAny))
|
|
||||||
for i, item := range dictAny {
|
|
||||||
d[i] = item
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
d = make(DictType)
|
|
||||||
}
|
|
||||||
dict = &d
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
|
||||||
indent := GetFormatIndent(opt)
|
|
||||||
flags := GetFormatFlags(opt)
|
|
||||||
//sb.WriteString(strings.Repeat(" ", indent))
|
|
||||||
sb.WriteByte('{')
|
|
||||||
|
|
||||||
if len(*dict) > 0 {
|
|
||||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
|
||||||
nest := strings.Repeat(" ", indent+1)
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
|
|
||||||
first := true
|
|
||||||
for name, value := range *dict {
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
sb.WriteByte(',')
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.WriteString(nest)
|
|
||||||
if key, ok := name.(string); ok {
|
|
||||||
sb.WriteString(string('"') + key + string('"'))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", name))
|
|
||||||
}
|
|
||||||
sb.WriteString(": ")
|
|
||||||
if f, ok := value.(Formatter); ok {
|
|
||||||
sb.WriteString(f.ToString(innerOpt))
|
|
||||||
} else if _, ok = value.(Functor); ok {
|
|
||||||
sb.WriteString("func(){}")
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
sb.WriteString(strings.Repeat(" ", indent))
|
|
||||||
}
|
|
||||||
sb.WriteString("}")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
flags := GetFormatFlags(opt)
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
dict.toMultiLine(&sb, opt)
|
|
||||||
} else {
|
|
||||||
sb.WriteByte('{')
|
|
||||||
first := true
|
|
||||||
for key, value := range *dict {
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
} else {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
if s, ok := key.(string); ok {
|
|
||||||
sb.WriteString(string('"') + s + string('"'))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", key))
|
|
||||||
}
|
|
||||||
sb.WriteString(": ")
|
|
||||||
if formatter, ok := value.(Formatter); ok {
|
|
||||||
sb.WriteString(formatter.ToString(opt))
|
|
||||||
} else if t, ok := value.(Term); ok {
|
|
||||||
sb.WriteString(t.String())
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%#v", value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteByte('}')
|
|
||||||
}
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) String() string {
|
|
||||||
return dict.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) TypeName() string {
|
|
||||||
return "dict"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) HasKey(target any) (ok bool) {
|
|
||||||
for key := range *dict {
|
|
||||||
if ok = reflect.DeepEqual(key, target); ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *DictType) Merge(second *DictType) {
|
|
||||||
if second != nil {
|
|
||||||
for k, v := range *second {
|
|
||||||
(*dict)[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////
|
|
||||||
|
|
||||||
type DictFormat interface {
|
|
||||||
ToDict() *DictType
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
// Copyright (c) 2024 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)
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// expr-function.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
// ---- Functor interface
|
|
||||||
type Functor interface {
|
|
||||||
Typer
|
|
||||||
InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error)
|
|
||||||
SetFunc(info ExprFunc)
|
|
||||||
GetFunc() ExprFunc
|
|
||||||
GetParams() []ExprFuncParam
|
|
||||||
GetDefinitionContext() ExprContext
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Function Param Info
|
|
||||||
type ExprFuncParam interface {
|
|
||||||
Name() string
|
|
||||||
Type() string
|
|
||||||
IsDefault() bool
|
|
||||||
IsOptional() bool
|
|
||||||
IsRepeat() bool
|
|
||||||
DefaultValue() any
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Function Info
|
|
||||||
type ExprFunc interface {
|
|
||||||
Formatter
|
|
||||||
Name() string
|
|
||||||
MinArgs() int
|
|
||||||
MaxArgs() int
|
|
||||||
Functor() Functor
|
|
||||||
Params() []ExprFuncParam
|
|
||||||
ParamSpec(paramName string) ExprFuncParam
|
|
||||||
ReturnType() string
|
|
||||||
PrepareCall(name string, actualParams map[string]any) (err error)
|
|
||||||
AllocContext(parentCtx ExprContext) (ctx ExprContext)
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
// 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,79 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// formatter.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
|
||||||
|
|
||||||
const (
|
|
||||||
TTY FmtOpt = 1 << iota
|
|
||||||
MultiLine
|
|
||||||
Truncate
|
|
||||||
Base2
|
|
||||||
Base8
|
|
||||||
Base10
|
|
||||||
Base16
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
TruncateEllipsis = "(...)"
|
|
||||||
MinTruncateSize = 10
|
|
||||||
TruncateSize = MinTruncateSize + 15
|
|
||||||
)
|
|
||||||
|
|
||||||
func TruncateString(s string) (trunc string) {
|
|
||||||
finalPart := len(s) - (MinTruncateSize - len(TruncateEllipsis))
|
|
||||||
trunc = s[0:len(s)-MinTruncateSize] + TruncateEllipsis + s[finalPart:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeFormatOptions(flags FmtOpt, indent int) FmtOpt {
|
|
||||||
return FmtOpt(indent<<16) | flags
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFormatFlags(opt FmtOpt) FmtOpt {
|
|
||||||
return opt & 0xFFFF
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFormatIndent(opt FmtOpt) int {
|
|
||||||
return int(opt >> 16)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Formatter interface {
|
|
||||||
ToString(options FmtOpt) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
|
||||||
if v == nil {
|
|
||||||
text = "(nil)"
|
|
||||||
} else if s, ok := v.(string); ok {
|
|
||||||
text = s
|
|
||||||
} else if formatter, ok := v.(Formatter); ok {
|
|
||||||
text = formatter.ToString(opt)
|
|
||||||
} else {
|
|
||||||
text = fmt.Sprintf("%v", v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Typer interface {
|
|
||||||
TypeName() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TypeName(v any) (name string) {
|
|
||||||
if v == nil {
|
|
||||||
name = "nil"
|
|
||||||
} else if typer, ok := v.(Typer); ok {
|
|
||||||
name = typer.TypeName()
|
|
||||||
} else if IsInteger(v) {
|
|
||||||
name = "integer"
|
|
||||||
} else if IsFloat(v) {
|
|
||||||
name = "float"
|
|
||||||
} else {
|
|
||||||
name = fmt.Sprintf("%T", v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,384 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// fraction-type.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FractionType struct {
|
|
||||||
num, den int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFraction(num, den int64) *FractionType {
|
|
||||||
num, den = simplifyIntegers(num, den)
|
|
||||||
return &FractionType{num, den}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Float64ToFraction(f float64) (fract *FractionType, err error) {
|
|
||||||
var sign string
|
|
||||||
intPart, decPart := math.Modf(f)
|
|
||||||
if decPart < 0.0 {
|
|
||||||
sign = "-"
|
|
||||||
intPart = -intPart
|
|
||||||
decPart = -decPart
|
|
||||||
}
|
|
||||||
dec := fmt.Sprintf("%.12f", decPart)
|
|
||||||
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
|
|
||||||
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) {
|
|
||||||
var num, den int64
|
|
||||||
var sign int64 = 1
|
|
||||||
var parts []string
|
|
||||||
if len(s) == 0 {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
if s[0] == '-' {
|
|
||||||
sign = int64(-1)
|
|
||||||
s = s[1:]
|
|
||||||
} else if s[0] == '+' {
|
|
||||||
s = s[1:]
|
|
||||||
}
|
|
||||||
// if strings.HasSuffix(s, "()") {
|
|
||||||
// s = s[0 : len(s)-2]
|
|
||||||
// }
|
|
||||||
s = strings.TrimSuffix(s, "()")
|
|
||||||
parts = strings.SplitN(s, ".", 2)
|
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(parts) == 1 {
|
|
||||||
f = NewFraction(sign*num, 1)
|
|
||||||
} else if len(parts) == 2 {
|
|
||||||
subParts := strings.SplitN(parts[1], "(", 2)
|
|
||||||
if len(subParts) == 1 {
|
|
||||||
den = 1
|
|
||||||
dec := parts[1]
|
|
||||||
lsd := len(dec)
|
|
||||||
for i := lsd - 1; i >= 0 && dec[i] == '0'; i-- {
|
|
||||||
lsd--
|
|
||||||
}
|
|
||||||
for _, c := range dec[0:lsd] {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, ErrExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
den = den * 10
|
|
||||||
}
|
|
||||||
f = NewFraction(sign*num, den)
|
|
||||||
} else if len(subParts) == 2 {
|
|
||||||
sub := num
|
|
||||||
mul := int64(1)
|
|
||||||
for _, c := range subParts[0] {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, ErrExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
sub = sub*10 + int64(c-'0')
|
|
||||||
mul *= 10
|
|
||||||
}
|
|
||||||
if len(subParts) == 2 {
|
|
||||||
if s[len(s)-1] != ')' {
|
|
||||||
goto exit
|
|
||||||
}
|
|
||||||
p := subParts[1][0 : len(subParts[1])-1]
|
|
||||||
for _, c := range p {
|
|
||||||
if c < '0' || c > '9' {
|
|
||||||
return nil, ErrExpectedGot("fract", "digit", c)
|
|
||||||
}
|
|
||||||
num = num*10 + int64(c-'0')
|
|
||||||
den = den*10 + 9
|
|
||||||
}
|
|
||||||
den *= mul
|
|
||||||
}
|
|
||||||
num -= sub
|
|
||||||
f = NewFraction(sign*num, den)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
if f == nil {
|
|
||||||
err = errors.New("bad syntax")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FractionType) String() string {
|
|
||||||
return f.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
|
||||||
var sb strings.Builder
|
|
||||||
if opt&MultiLine == 0 {
|
|
||||||
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
|
||||||
} else {
|
|
||||||
var sign, num string
|
|
||||||
if f.num < 0 && opt&TTY == 0 {
|
|
||||||
num = strconv.FormatInt(-f.num, 10)
|
|
||||||
sign = "-"
|
|
||||||
} else {
|
|
||||||
num = strconv.FormatInt(f.num, 10)
|
|
||||||
}
|
|
||||||
den := strconv.FormatInt(f.den, 10)
|
|
||||||
size := max(len(num), len(den))
|
|
||||||
if opt&TTY != 0 {
|
|
||||||
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
|
|
||||||
sb.WriteString(sNum)
|
|
||||||
} else {
|
|
||||||
if len(sign) > 0 {
|
|
||||||
sb.WriteString(" ")
|
|
||||||
}
|
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
if len(sign) > 0 {
|
|
||||||
sb.WriteString(sign)
|
|
||||||
sb.WriteByte(' ')
|
|
||||||
}
|
|
||||||
sb.WriteString(strings.Repeat("-", size))
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
if len(sign) > 0 {
|
|
||||||
sb.WriteString(" ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
|
|
||||||
sb.WriteString(sDen)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FractionType) TypeName() string {
|
|
||||||
return "fraction"
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- fraction utility functions
|
|
||||||
|
|
||||||
// greatest common divider
|
|
||||||
func Gcd(a, b int64) (g int64) {
|
|
||||||
if a < 0 {
|
|
||||||
a = -a
|
|
||||||
}
|
|
||||||
if b < 0 {
|
|
||||||
b = -b
|
|
||||||
}
|
|
||||||
if a < b {
|
|
||||||
a, b = b, a
|
|
||||||
}
|
|
||||||
r := a % b
|
|
||||||
for r > 0 {
|
|
||||||
a, b = b, r
|
|
||||||
r = a % b
|
|
||||||
}
|
|
||||||
g = b
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// lower common multiple
|
|
||||||
func lcm(a, b int64) (l int64) {
|
|
||||||
g := Gcd(a, b)
|
|
||||||
l = a * b / g
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sum two fractions
|
|
||||||
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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Multiply two fractions
|
|
||||||
func MulFract(f1, f2 *FractionType) (prod *FractionType) {
|
|
||||||
prod = NewFraction(f1.num*f2.num, f1.den*f2.den)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyToFract(v any) (f *FractionType, err error) {
|
|
||||||
var ok bool
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
|
|
||||||
if f1, err = anyToFract(v1); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f2, err = anyToFract(v2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if f.num == 0 {
|
|
||||||
sum = 0
|
|
||||||
} else {
|
|
||||||
sum = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns
|
|
||||||
//
|
|
||||||
// <0 if af1 < af2
|
|
||||||
// =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) {
|
|
||||||
var f1, f2 *FractionType
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
result = cmpFract(f1, f2)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns
|
|
||||||
//
|
|
||||||
// <0 if af1 < af2
|
|
||||||
// =0 if af1 == af2
|
|
||||||
// >0 if af1 > af2
|
|
||||||
func cmpFract(f1, f2 *FractionType) (result int) {
|
|
||||||
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) {
|
|
||||||
var f1, f2 *FractionType
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f2.num = -f2.num
|
|
||||||
f := SumFract(f1, f2)
|
|
||||||
if f.num == 0 {
|
|
||||||
sum = 0
|
|
||||||
} else {
|
|
||||||
sum = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MulAnyFract(af1, af2 any) (prod any, err error) {
|
|
||||||
var f1, f2 *FractionType
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f1.num == 0 || f2.num == 0 {
|
|
||||||
prod = 0
|
|
||||||
} else {
|
|
||||||
f := &FractionType{f1.num * f2.num, f1.den * f2.den}
|
|
||||||
prod = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func DivAnyFract(af1, af2 any) (quot any, err error) {
|
|
||||||
var f1, f2 *FractionType
|
|
||||||
if f1, f2, err = anyPairToFract(af1, af2); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f2.num == 0 {
|
|
||||||
err = errors.New("division by zero")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if f1.num == 0 || f2.den == 0 {
|
|
||||||
quot = 0
|
|
||||||
} else {
|
|
||||||
f := &FractionType{f1.num * f2.den, f1.den * f2.num}
|
|
||||||
quot = simplifyFraction(f)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func simplifyFraction(f *FractionType) (v any) {
|
|
||||||
f.num, f.den = simplifyIntegers(f.num, f.den)
|
|
||||||
if f.den == 1 {
|
|
||||||
v = f.num
|
|
||||||
} else {
|
|
||||||
v = &FractionType{f.num, f.den}
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func simplifyIntegers(num, den int64) (a, b int64) {
|
|
||||||
if num == 0 {
|
|
||||||
return 0, 1
|
|
||||||
}
|
|
||||||
if den == 0 {
|
|
||||||
panic("fraction with denominator == 0")
|
|
||||||
}
|
|
||||||
if den < 0 {
|
|
||||||
den = -den
|
|
||||||
num = -num
|
|
||||||
}
|
|
||||||
g := Gcd(num, den)
|
|
||||||
a = num / g
|
|
||||||
b = den / g
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func intToFraction(n int64) *FractionType {
|
|
||||||
return &FractionType{n, 1}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
278
kern/function.go
278
kern/function.go
@ -1,278 +0,0 @@
|
|||||||
// Copyright (c) 2024 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)
|
|
||||||
|
|
||||||
func IsFunctor(v any) (ok bool) {
|
|
||||||
_, ok = v.(Functor)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func initActualParams(ctx ExprContext, info ExprFunc, callTerm Term) (actualParams map[string]any, err error) {
|
|
||||||
var varArgs []any
|
|
||||||
var varName string
|
|
||||||
|
|
||||||
namedParamsStarted := false
|
|
||||||
|
|
||||||
formalParams := info.Params()
|
|
||||||
actualParams = make(map[string]any, len(formalParams))
|
|
||||||
if callTerm == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
childCount := callTerm.GetChildCount()
|
|
||||||
for i := range childCount {
|
|
||||||
tree := callTerm.GetChild(i)
|
|
||||||
// for i, tree := range callTerm.Children() {
|
|
||||||
var paramValue any
|
|
||||||
paramCtx := ctx.Clone()
|
|
||||||
if paramValue, err = tree.Compute(paramCtx); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if paramName, namedParam := GetAssignVarName(tree); namedParam {
|
|
||||||
if info.ParamSpec(paramName) == nil {
|
|
||||||
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
actualParams[paramName] = paramValue
|
|
||||||
namedParamsStarted = true
|
|
||||||
} else if !namedParamsStarted {
|
|
||||||
if varArgs != nil {
|
|
||||||
varArgs = append(varArgs, paramValue)
|
|
||||||
} else if i < len(formalParams) {
|
|
||||||
spec := formalParams[i]
|
|
||||||
if spec.IsRepeat() {
|
|
||||||
varArgs = make([]any, 0, childCount-i)
|
|
||||||
varArgs = append(varArgs, paramValue)
|
|
||||||
varName = spec.Name()
|
|
||||||
} else {
|
|
||||||
actualParams[spec.Name()] = paramValue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = ErrTooManyParams(info.Name(), len(formalParams), childCount)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if varArgs != nil {
|
|
||||||
actualParams[varName] = varArgs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
|
|
||||||
// passedCount := len(actualParams)
|
|
||||||
// if info.MinArgs() > passedCount {
|
|
||||||
// err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if passedCount < len(info.formalParams) {
|
|
||||||
// for _, p := range info.formalParams {
|
|
||||||
// if _, exists := actualParams[p.Name()]; !exists {
|
|
||||||
// if !p.IsDefault() {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// if p.IsRepeat() {
|
|
||||||
// varArgs := make([]any, 1)
|
|
||||||
// varArgs[0] = p.DefaultValue()
|
|
||||||
// actualParams[p.Name()] = varArgs
|
|
||||||
// } else {
|
|
||||||
// actualParams[p.Name()] = p.DefaultValue()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
|
|
||||||
// err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ----- Call a function ---
|
|
||||||
|
|
||||||
// func getAssignVarName(t *term) (name string, ok bool) {
|
|
||||||
// if ok = t.symbol() == SymEqual; ok {
|
|
||||||
// name = t.children[0].source()
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func GetAssignVarName(t Term) (name string, ok bool) {
|
|
||||||
if ok = t.IsAssign(); ok {
|
|
||||||
name = t.GetChildSource(0)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm Term) (result any, err error) {
|
|
||||||
var actualParams map[string]any
|
|
||||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
|
||||||
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
|
|
||||||
ctx := info.AllocContext(parentCtx)
|
|
||||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
|
||||||
functor := info.Functor()
|
|
||||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
|
||||||
exportObjectsToParent(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
|
|
||||||
var actualParams map[string]any
|
|
||||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
|
||||||
functor := info.Functor()
|
|
||||||
actualParams = BindActualParams(functor, args)
|
|
||||||
ctx := info.AllocContext(parentCtx)
|
|
||||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
|
||||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
|
||||||
exportObjectsToParent(ctx)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
|
|
||||||
//var actualParams map[string]any
|
|
||||||
if info, exists := parentCtx.GetFuncInfo(name); exists {
|
|
||||||
functor := info.Functor()
|
|
||||||
ctx := info.AllocContext(parentCtx)
|
|
||||||
if err = info.PrepareCall(name, actualParams); err == nil {
|
|
||||||
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
|
||||||
exportObjectsToParent(ctx)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
|
|
||||||
if value, exists = args[paramName]; !exists {
|
|
||||||
if paramNum > 0 && paramNum <= len(args) {
|
|
||||||
value, exists = args["arg"+strconv.Itoa(paramNum)]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func BindActualParams(functor Functor, args []any) (actualParams map[string]any) {
|
|
||||||
formalParams := functor.GetParams()
|
|
||||||
actualParams = make(map[string]any, len(args))
|
|
||||||
for i, arg := range args {
|
|
||||||
if i < len(formalParams) {
|
|
||||||
actualParams[formalParams[i].Name()] = arg
|
|
||||||
} else {
|
|
||||||
actualParams["arg"+strconv.Itoa(i+1)] = arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// iterator.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
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() 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
|
|
||||||
}
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// list-type.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListType []any
|
|
||||||
|
|
||||||
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) {
|
|
||||||
if listAny != nil {
|
|
||||||
ls := make(ListType, len(listAny))
|
|
||||||
copy(ls, listAny)
|
|
||||||
list = &ls
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeList(length, capacity int) (list *ListType) {
|
|
||||||
if capacity < length {
|
|
||||||
capacity = length
|
|
||||||
}
|
|
||||||
ls := make(ListType, length, capacity)
|
|
||||||
list = &ls
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListFromStrings(stringList []string) (list *ListType) {
|
|
||||||
list = MakeList(len(stringList), 0)
|
|
||||||
for i, s := range stringList {
|
|
||||||
(*list)[i] = s
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) ToString(opt FmtOpt) (s string) {
|
|
||||||
indent := GetFormatIndent(opt)
|
|
||||||
flags := GetFormatFlags(opt)
|
|
||||||
|
|
||||||
var sb strings.Builder
|
|
||||||
sb.WriteByte('[')
|
|
||||||
if len(*dict) > 0 {
|
|
||||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
|
||||||
nest := strings.Repeat(" ", indent+1)
|
|
||||||
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
sb.WriteString(nest)
|
|
||||||
}
|
|
||||||
for i, item := range []any(*dict) {
|
|
||||||
if i > 0 {
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteString(",\n")
|
|
||||||
sb.WriteString(nest)
|
|
||||||
} else {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
sb.WriteString(strings.Repeat(" ", indent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteByte(']')
|
|
||||||
s = sb.String()
|
|
||||||
if flags&Truncate != 0 && len(s) > TruncateSize {
|
|
||||||
s = TruncateString(s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) String() string {
|
|
||||||
return dict.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) TypeName() string {
|
|
||||||
return "list"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
|
||||||
if len(*dict) >= len(*t) {
|
|
||||||
answer = true
|
|
||||||
for _, item := range *t {
|
|
||||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
|
||||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
|
||||||
answer = true
|
|
||||||
for index, i1 := range *ls1 {
|
|
||||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
|
||||||
// answer = false
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
if !Equal(i1, ls2[index]) {
|
|
||||||
answer = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
|
||||||
var eq bool
|
|
||||||
var err error
|
|
||||||
index = -1
|
|
||||||
for i, item := range *dict {
|
|
||||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
|
||||||
break
|
|
||||||
} else if eq {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
same = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var cmp int
|
|
||||||
if cmp, err = CmpAnyFract(a, b); err == nil {
|
|
||||||
eq = cmp == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if deepCmp != nil && IsList(a) && IsList(b) {
|
|
||||||
eq, err = deepCmp(a, b)
|
|
||||||
} else {
|
|
||||||
eq = reflect.DeepEqual(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
|
||||||
if index >= 0 && index < int64(len(*dict)) {
|
|
||||||
(*dict)[index] = value
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) AppendItem(value any) {
|
|
||||||
*dict = append(*dict, value)
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// number.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsInteger(v any) (ok bool) {
|
|
||||||
_, ok = v.(int64)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNumber(v any) (ok bool) {
|
|
||||||
return IsFloat(v) || IsInteger(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNumOrFract(v any) (ok bool) {
|
|
||||||
return IsFloat(v) || IsInteger(v) || IsFraction(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNumberString(v any) (ok bool) {
|
|
||||||
return IsString(v) || IsNumber(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NumAsFloat(v any) (f float64) {
|
|
||||||
var ok bool
|
|
||||||
if f, ok = v.(float64); !ok {
|
|
||||||
if fract, ok := v.(*FractionType); ok {
|
|
||||||
f = fract.ToFloat()
|
|
||||||
} else {
|
|
||||||
i, _ := v.(int64)
|
|
||||||
f = float64(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func AnyInteger(v any) (i int64, ok bool) {
|
|
||||||
ok = true
|
|
||||||
switch intval := v.(type) {
|
|
||||||
case int:
|
|
||||||
i = int64(intval)
|
|
||||||
case uint8:
|
|
||||||
i = int64(intval)
|
|
||||||
case uint16:
|
|
||||||
i = int64(intval)
|
|
||||||
case uint64:
|
|
||||||
i = int64(intval)
|
|
||||||
case uint32:
|
|
||||||
i = int64(intval)
|
|
||||||
case int8:
|
|
||||||
i = int64(intval)
|
|
||||||
case int16:
|
|
||||||
i = int64(intval)
|
|
||||||
case int32:
|
|
||||||
i = int64(intval)
|
|
||||||
case int64:
|
|
||||||
i = intval
|
|
||||||
default:
|
|
||||||
ok = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToGoInt(value any, description string) (i int, err error) {
|
|
||||||
if valueInt64, ok := value.(int64); ok {
|
|
||||||
i = int(valueInt64)
|
|
||||||
} else if valueInt, ok := value.(int); ok {
|
|
||||||
i = valueInt
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToGoInt64(value any, description string) (i int64, err error) {
|
|
||||||
if valueInt64, ok := value.(int64); ok {
|
|
||||||
i = valueInt64
|
|
||||||
} else if valueInt, ok := value.(int); ok {
|
|
||||||
i = int64(valueInt)
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// string.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsString(v any) (ok bool) {
|
|
||||||
_, ok = v.(string)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToGoString(value any, description string) (s string, err error) {
|
|
||||||
if s, ok := value.(string); ok {
|
|
||||||
return s, nil
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
21
kern/term.go
21
kern/term.go
@ -1,21 +0,0 @@
|
|||||||
// Copyright (c) 2024 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,9 +0,0 @@
|
|||||||
//go:build darwin
|
|
||||||
|
|
||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// lib-ext-darwin.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
const SHAREDLIBRARY_EXTENSION = ".dylib"
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
//go:build linux
|
|
||||||
|
|
||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// lib-ext-linux.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
const SHAREDLIBRARY_EXTENSION = ".so"
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
|
|
||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// lib-ext-windows.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
const SHAREDLIBRARY_EXTENSION = ".dll"
|
|
||||||
149
list-iterator.go
149
list-iterator.go
@ -1,149 +0,0 @@
|
|||||||
// Copyright (c) 2024 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 ListIterator struct {
|
|
||||||
a *kern.ListType
|
|
||||||
count int64
|
|
||||||
index int64
|
|
||||||
start int64
|
|
||||||
stop int64
|
|
||||||
step int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
|
|
||||||
var argc int = 0
|
|
||||||
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 := kern.ToGoInt64(args[0], "start index"); err == nil {
|
|
||||||
if i < 0 {
|
|
||||||
i = listLen + i
|
|
||||||
}
|
|
||||||
it.start = i
|
|
||||||
}
|
|
||||||
if argc >= 2 {
|
|
||||||
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 := kern.ToGoInt64(args[2], "step"); err == nil {
|
|
||||||
if i < 0 {
|
|
||||||
i = -i
|
|
||||||
}
|
|
||||||
if it.start > it.stop {
|
|
||||||
it.step = -i
|
|
||||||
} else {
|
|
||||||
it.step = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it.index = it.start - it.step
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArrayIterator(array []any) (it *ListIterator) {
|
|
||||||
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 = int64(0)
|
|
||||||
if it.a != nil {
|
|
||||||
l = int64(len(*it.a))
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("$([#%d])", l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) TypeName() string {
|
|
||||||
return "ListIterator"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) HasOperation(name string) bool {
|
|
||||||
//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 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 *ListIterator) Current() (item any, err error) {
|
|
||||||
a := *(it.a)
|
|
||||||
if it.start <= 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 < int64(len(a)) && it.index >= it.stop && it.index <= it.start {
|
|
||||||
item = a[it.index]
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) Next() (item any, err error) {
|
|
||||||
it.index += it.step
|
|
||||||
if item, err = it.Current(); err != io.EOF {
|
|
||||||
it.count++
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) Index() int64 {
|
|
||||||
return it.index
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) Count() int64 {
|
|
||||||
return it.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) Reset() error {
|
|
||||||
it.index = it.start - it.step
|
|
||||||
it.count = 0
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *ListIterator) Clean() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
73
operand-const.go
Normal file
73
operand-const.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-const.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// -------- bool const term
|
||||||
|
func newBoolTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
// class: classConst,
|
||||||
|
// kind: kindBool,
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalConst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- integer const term
|
||||||
|
func newIntegerTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalConst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- float const term
|
||||||
|
func newFloatTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
// class: classConst,
|
||||||
|
// kind: kindFloat,
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalConst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- string const term
|
||||||
|
func newStringTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
// class: classConst,
|
||||||
|
// kind: kindString,
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalConst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- eval func
|
||||||
|
func evalConst(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
v = self.tk.Value
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymString, newStringTerm)
|
||||||
|
registerTermConstructor(SymInteger, newIntegerTerm)
|
||||||
|
registerTermConstructor(SymFloat, newFloatTerm)
|
||||||
|
registerTermConstructor(SymBool, newBoolTerm)
|
||||||
|
}
|
||||||
@ -1,38 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operand-dict.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- dict func
|
|
||||||
func evalDict(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
dict, _ := opTerm.value().(map[any]*term)
|
|
||||||
items := make(kern.DictType, len(dict))
|
|
||||||
for key, tree := range dict {
|
|
||||||
var param any
|
|
||||||
if param, err = tree.Compute(ctx); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
items[key] = param
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
v = &items
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -4,15 +4,10 @@
|
|||||||
// operand-expr.go
|
// operand-expr.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- expr term
|
// -------- expr term
|
||||||
func newExprTerm(root *term) *term {
|
func newExprTerm(tk *Token) *term {
|
||||||
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.Source(), root)
|
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
@ -24,11 +19,16 @@ func newExprTerm(root *term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval expr
|
// -------- eval expr
|
||||||
func evalExpr(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalExpr(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if ast, ok := opTerm.value().(*term); ok {
|
if expr, ok := self.value().(Expr); ok {
|
||||||
v, err = ast.Compute(ctx)
|
v, err = expr.eval(ctx, false)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("expression expected, got %T", opTerm.value())
|
err = fmt.Errorf("expression expected, got %T", self.value())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
// func init() {
|
||||||
|
// registerTermConstructor(SymExpression, newExprTerm)
|
||||||
|
// }
|
||||||
|
|||||||
124
operand-func.go
124
operand-func.go
@ -6,56 +6,73 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- function call term
|
// -------- function call term
|
||||||
func newFuncCallTerm(tk *Token, args []*term) *term {
|
func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||||
var pos termPosition = posLeaf
|
|
||||||
if len(args) > 0 {
|
|
||||||
pos = posMultifix
|
|
||||||
}
|
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
|
// class: classVar,
|
||||||
|
// kind: kindUnknown,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: args,
|
children: args,
|
||||||
position: pos,
|
position: posLeaf,
|
||||||
priority: priValue,
|
priority: priValue,
|
||||||
evalFunc: evalFuncCall,
|
evalFunc: evalFuncCall,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
||||||
// name, _ := opTerm.tk.Value.(string)
|
ctx := parentCtx.Clone()
|
||||||
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
|
name, _ := self.tk.Value.(string)
|
||||||
// for i, tree := range opTerm.children {
|
params := make([]any, len(self.children))
|
||||||
// var param any
|
for i, tree := range self.children {
|
||||||
// if param, err = tree.compute(ctx); err != nil {
|
var param any
|
||||||
// break
|
if param, err = tree.compute(ctx); err != nil {
|
||||||
// }
|
break
|
||||||
// params[i] = param
|
}
|
||||||
// }
|
params[i] = param
|
||||||
|
}
|
||||||
// if err == nil {
|
if err == nil {
|
||||||
// v, err = CallFunction(ctx, name, params)
|
if v, err = ctx.Call(name, params); err == nil {
|
||||||
// }
|
exportAll := isEnabled(ctx, control_export_all)
|
||||||
// return
|
// Export variables
|
||||||
// }
|
for _, refName := range ctx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||||
|
refValue, _ := ctx.GetVar(refName)
|
||||||
func evalFuncCall(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
exportVar(parentCtx, refName, refValue)
|
||||||
name, _ := opTerm.tk.Value.(string)
|
}
|
||||||
v, err = kern.CallFunctionByTerm(ctx, name, opTerm)
|
// Export functions
|
||||||
|
for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||||
|
if info := ctx.GetFuncInfo(refName); info != nil {
|
||||||
|
exportFunc(parentCtx, refName, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exportVar(ctx ExprContext, name string, value any) {
|
||||||
|
if name[0] == '@' {
|
||||||
|
name = name[1:]
|
||||||
|
}
|
||||||
|
ctx.setVar(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
||||||
|
if name[0] == '@' {
|
||||||
|
name = name[1:]
|
||||||
|
}
|
||||||
|
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||||
|
}
|
||||||
|
|
||||||
// -------- function definition term
|
// -------- function definition term
|
||||||
func newFuncDefTerm(tk *Token, args []*term) *term {
|
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk, // value is the expression body
|
tk: *tk,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: args, // function params
|
children: args, // arg[0]=formal-param-list, arg[1]=*ast
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
priority: priValue,
|
priority: priValue,
|
||||||
evalFunc: evalFuncDef,
|
evalFunc: evalFuncDef,
|
||||||
@ -63,23 +80,46 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func def
|
// -------- eval func def
|
||||||
func evalFuncDef(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
// TODO
|
||||||
bodySpec := opTerm.value()
|
type funcDefFunctor struct {
|
||||||
if ast, ok := bodySpec.(*ast); ok {
|
params []string
|
||||||
paramList := make([]kern.ExprFuncParam, 0, len(opTerm.children))
|
expr Expr
|
||||||
for _, param := range opTerm.children {
|
}
|
||||||
var defValue any
|
|
||||||
flags := paramFlags(0)
|
func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
if len(param.children) > 0 {
|
for i, p := range functor.params {
|
||||||
flags |= PfDefault
|
if i < len(args) {
|
||||||
if defValue, err = param.children[0].Compute(ctx); err != nil {
|
arg := args[i]
|
||||||
|
if functor, ok := arg.(Functor); ok {
|
||||||
|
ctx.RegisterFunc(p, functor, 0, -1)
|
||||||
|
} else {
|
||||||
|
ctx.setVar(p, arg)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.setVar(p, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err = functor.expr.eval(ctx, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
bodySpec := self.value()
|
||||||
|
if expr, ok := bodySpec.(*ast); ok {
|
||||||
|
paramList := make([]string, 0, len(self.children))
|
||||||
|
for _, param := range self.children {
|
||||||
|
paramList = append(paramList, param.source())
|
||||||
|
// if paramName, ok := param.value().(string); ok {
|
||||||
|
// paramList = append(paramList, paramName)
|
||||||
|
// } else {
|
||||||
|
// err = fmt.Errorf("invalid function definition: formal param nr %d must be an identifier", i+1)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
info := NewFuncParamFlagDef(param.Source(), flags, defValue)
|
v = &funcDefFunctor{
|
||||||
paramList = append(paramList, info)
|
params: paramList,
|
||||||
|
expr: expr,
|
||||||
}
|
}
|
||||||
v = newExprFunctor(ast, paramList, ctx)
|
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("invalid function definition: the body specification must be an expression")
|
err = errors.New("invalid function definition: the body specification must be an expression")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,171 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operand-iterator.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- eval iterator
|
|
||||||
|
|
||||||
func evalTermArray(ctx kern.ExprContext, terms []*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 {
|
|
||||||
values[i] = value
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalFirstChild(ctx kern.ExprContext, iteratorTerm *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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDataSourceDict(iteratorTerm *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]kern.Functor)
|
|
||||||
for keyAny, item := range *dictAny {
|
|
||||||
if key, ok := keyAny.(string); ok {
|
|
||||||
if functor, ok := item.(kern.Functor); ok {
|
|
||||||
ds[key] = functor
|
|
||||||
if index := slices.Index(requiredFields, key); index >= 0 {
|
|
||||||
foundFields |= 1 << index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// check required functions
|
|
||||||
if foundFields != fieldsMask {
|
|
||||||
missingFields := make([]string, 0, len(requiredFields))
|
|
||||||
for index, field := range requiredFields {
|
|
||||||
if (foundFields & (1 << index)) == 0 {
|
|
||||||
missingFields = append(missingFields, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalIterator(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var firstChildValue any
|
|
||||||
var ds map[string]kern.Functor
|
|
||||||
|
|
||||||
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = nil
|
|
||||||
|
|
||||||
if ds != nil {
|
|
||||||
if len(ds) > 0 {
|
|
||||||
var dc *dataCursor
|
|
||||||
dcCtx := ctx.Clone()
|
|
||||||
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 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args = []any{}
|
|
||||||
}
|
|
||||||
|
|
||||||
actualParams := kern.BindActualParams(initFunc, args)
|
|
||||||
|
|
||||||
initCtx := ctx.Clone()
|
|
||||||
if resource, err = initFunc.InvokeNamed(initCtx, kern.InitName, actualParams); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
kern.ExportObjects(dcCtx, initCtx)
|
|
||||||
dc = NewDataCursor(dcCtx, ds, resource)
|
|
||||||
} else {
|
|
||||||
dc = NewDataCursor(dcCtx, ds, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
v = dc
|
|
||||||
} else {
|
|
||||||
if dictIt, ok := firstChildValue.(*kern.DictType); ok {
|
|
||||||
var args []any
|
|
||||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
|
||||||
v, err = NewDictIterator(dictIt, args)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.children[0].Errorf("the data-source must be a dictionary")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if list, ok := firstChildValue.(*kern.ListType); ok {
|
|
||||||
var args []any
|
|
||||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
|
||||||
v = NewListIterator(list, args)
|
|
||||||
}
|
|
||||||
} else if intVal, ok := firstChildValue.(int64); ok {
|
|
||||||
var args []any
|
|
||||||
if args, err = evalSibling(ctx, opTerm.children, intVal); err == nil {
|
|
||||||
v, err = NewIntIterator(args)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var list []any
|
|
||||||
if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
|
|
||||||
v = NewArrayIterator(list)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalSibling(ctx kern.ExprContext, terms []*term, firstChildValue any) (list []any, err error) {
|
|
||||||
items := make([]any, 0, len(terms))
|
|
||||||
for i, tree := range terms {
|
|
||||||
var param any
|
|
||||||
if i == 0 {
|
|
||||||
if firstChildValue == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
param = firstChildValue
|
|
||||||
} else if param, err = tree.Compute(ctx); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
items = append(items, param)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
list = items
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@ -4,18 +4,14 @@
|
|||||||
// operand-list.go
|
// operand-list.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- list term
|
// -------- list term
|
||||||
func newListTermA(args ...*term) *term {
|
func newListTermA(args ...*term) *term {
|
||||||
return newListTerm(0, 0, args)
|
return newListTerm(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newListTerm(row, col int, args []*term) *term {
|
func newListTerm(args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *NewValueToken(row, col, SymList, "[]", args),
|
tk: *NewValueToken(0, 0, SymList, "[]", args),
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: nil,
|
children: nil,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
@ -25,18 +21,46 @@ func newListTerm(row, col int, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- list func
|
// -------- list func
|
||||||
func evalList(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalList(ctx ExprContext, self *term) (v any, err error) {
|
||||||
list, _ := opTerm.value().([]*term)
|
list, _ := self.value().([]*term)
|
||||||
items := make(kern.ListType, len(list))
|
items := make([]any, len(list))
|
||||||
for i, tree := range list {
|
for i, tree := range list {
|
||||||
var param any
|
var param any
|
||||||
if param, err = tree.Compute(ctx); err != nil {
|
if param, err = tree.compute(ctx); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
items[i] = param
|
items[i] = param
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v = &items
|
v = items
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // -------- list term
|
||||||
|
// func newListTerm(args []*term) *term {
|
||||||
|
// return &term{
|
||||||
|
// tk: *NewToken(0, 0, SymList, "[]"),
|
||||||
|
// parent: nil,
|
||||||
|
// children: args,
|
||||||
|
// position: posLeaf,
|
||||||
|
// priority: priValue,
|
||||||
|
// evalFunc: evalList,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // -------- list func
|
||||||
|
// func evalList(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
// items := make([]any, len(self.children))
|
||||||
|
// for i, tree := range self.children {
|
||||||
|
// var param any
|
||||||
|
// if param, err = tree.compute(ctx); err != nil {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// items[i] = param
|
||||||
|
// }
|
||||||
|
// if err == nil {
|
||||||
|
// v = items
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|||||||
@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operand-literal.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- literal term
|
|
||||||
func newLiteralTerm(tk *Token) *term {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
parent: nil,
|
|
||||||
children: nil,
|
|
||||||
position: posLeaf,
|
|
||||||
priority: priValue,
|
|
||||||
evalFunc: evalLiteral,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- eval func
|
|
||||||
func evalLiteral(ctx kern.ExprContext, opTerm *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)
|
|
||||||
}
|
|
||||||
@ -7,8 +7,6 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- selector case term
|
// -------- selector case term
|
||||||
@ -43,10 +41,10 @@ func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval selector case
|
// -------- eval selector case
|
||||||
func evalSelectorCase(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalSelectorCase(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
if v, ok = opTerm.value().(*selectorCase); !ok {
|
if v, ok = self.value().(*selectorCase); !ok {
|
||||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
|
err = fmt.Errorf("selector-case expected, got %T", self.value())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,36 +4,27 @@
|
|||||||
// operand-var.go
|
// operand-var.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- variable term
|
// -------- variable term
|
||||||
func newVarTerm(tk *Token) *term {
|
func newVarTerm(tk *Token) *term {
|
||||||
t := &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
|
// class: classVar,
|
||||||
|
// kind: kindUnknown,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: nil,
|
children: nil,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
priority: priValue,
|
priority: priValue,
|
||||||
evalFunc: evalVar,
|
evalFunc: evalVar,
|
||||||
}
|
}
|
||||||
t.tk.Sym = SymVariable
|
|
||||||
return t
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalVar(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
name := opTerm.Source()
|
if v, exists = ctx.GetVar(self.tk.source); !exists {
|
||||||
if v, exists = ctx.GetVar(name); !exists {
|
err = fmt.Errorf("undefined variable %q", self.tk.source)
|
||||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
|
||||||
v = info.Functor()
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("undefined variable or function %q", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserightChilded.
|
// All rights reserved.
|
||||||
|
|
||||||
// operator-assign.go
|
// operator-assign.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- assign term
|
//-------- assign term
|
||||||
|
|
||||||
func newAssignTerm(tk *Token) (inst *term) {
|
func newAssignTerm(tk *Token) (inst *term) {
|
||||||
@ -21,189 +16,22 @@ func newAssignTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *term, value any) (err error) {
|
func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var collectionValue, keyListValue, keyValue any
|
if err = self.checkOperands(); err != nil {
|
||||||
var keyList *kern.ListType
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
leftTerm := self.children[0]
|
||||||
return
|
if leftTerm.tk.Sym != SymIdentifier {
|
||||||
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
|
||||||
err = keyListTerm.Errorf("index/key is nil")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch collection := collectionValue.(type) {
|
if v, err = self.children[1].compute(ctx); err == nil {
|
||||||
case *kern.ListType:
|
if functor, ok := v.(Functor); ok {
|
||||||
if index, ok := keyValue.(int64); ok {
|
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
err = collection.SetItem(index, value)
|
|
||||||
} else {
|
} else {
|
||||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
ctx.setVar(leftTerm.tk.source, v)
|
||||||
}
|
|
||||||
case *kern.DictType:
|
|
||||||
collection.SetItem(keyValue, value)
|
|
||||||
default:
|
|
||||||
err = collectionTerm.Errorf("collection expected")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func assignValue(ctx kern.ExprContext, leftTerm *term, v any) (err error) {
|
|
||||||
if leftTerm.symbol() == SymIndex {
|
|
||||||
err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v)
|
|
||||||
} else {
|
|
||||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalAssign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := opTerm.children[0]
|
|
||||||
leftSym := leftTerm.symbol()
|
|
||||||
if leftSym != SymVariable && leftSym != SymIndex {
|
|
||||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rightChild := opTerm.children[1]
|
|
||||||
|
|
||||||
if v, err = rightChild.Compute(ctx); err == nil {
|
|
||||||
if functor, ok := v.(kern.Functor); ok {
|
|
||||||
if leftSym == SymVariable {
|
|
||||||
if info := functor.GetFunc(); info != nil {
|
|
||||||
ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
|
|
||||||
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
|
||||||
paramSpecs := util.ForAll(funcDef.params, func(p kern.ExprFuncParam) kern.ExprFuncParam { return p })
|
|
||||||
|
|
||||||
ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, paramSpecs)
|
|
||||||
} else {
|
|
||||||
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = assignValue(ctx, leftTerm, v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = assignValue(ctx, leftTerm, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
v = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- assign term
|
|
||||||
|
|
||||||
func newOpAssignTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priAssign,
|
|
||||||
evalFunc: evalOpAssign,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
|
||||||
var collectionValue, keyListValue, keyValue any
|
|
||||||
var keyList *kern.ListType
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
} 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 {
|
|
||||||
err = keyListTerm.Errorf("index/key is nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch collection := collectionValue.(type) {
|
|
||||||
case *kern.ListType:
|
|
||||||
if index, ok := keyValue.(int64); ok {
|
|
||||||
value = (*collection)[index]
|
|
||||||
} else {
|
|
||||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
|
||||||
}
|
|
||||||
case *kern.DictType:
|
|
||||||
value = (*collection)[keyValue]
|
|
||||||
default:
|
|
||||||
err = collectionTerm.Errorf("collection expected")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAssignValue(ctx kern.ExprContext, leftTerm *term) (value any, err error) {
|
|
||||||
if leftTerm.symbol() == SymIndex {
|
|
||||||
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
|
||||||
} else {
|
|
||||||
value, _ = ctx.GetVar(leftTerm.Source())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalOpAssign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var rightValue, leftValue any
|
|
||||||
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)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
rightChild := opTerm.children[1]
|
|
||||||
|
|
||||||
if rightValue, err = rightChild.Compute(ctx); err == nil {
|
|
||||||
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
|
||||||
switch opTerm.symbol() {
|
|
||||||
case SymPlusEqual:
|
|
||||||
v, err = sumValues(opTerm, leftValue, rightValue)
|
|
||||||
case SymMinusEqual:
|
|
||||||
v, err = diffValues(opTerm, leftValue, rightValue)
|
|
||||||
case SymStarEqual:
|
|
||||||
v, err = mulValues(opTerm, leftValue, rightValue)
|
|
||||||
case SymSlashEqual:
|
|
||||||
v, err = divValues(opTerm, leftValue, rightValue)
|
|
||||||
case SymPercEqual:
|
|
||||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
|
||||||
case SymAmpersandEqual:
|
|
||||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
|
||||||
case SymVertBarEqual:
|
|
||||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
|
||||||
case SymCaretEqual:
|
|
||||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
|
||||||
case SymDoubleLessEqual:
|
|
||||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
|
||||||
case SymDoubleGreaterEqual:
|
|
||||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
|
||||||
default:
|
|
||||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
err = assignValue(ctx, leftTerm, v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -212,14 +40,4 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymEqual, newAssignTerm)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,158 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-bitwise.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- 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 evalBitwiseNot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var value any
|
|
||||||
|
|
||||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if kern.IsInteger(value) {
|
|
||||||
i, _ := value.(int64)
|
|
||||||
v = ^i
|
|
||||||
} else {
|
|
||||||
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 bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
|
||||||
var leftInt, rightInt int64
|
|
||||||
var lok, rok bool
|
|
||||||
|
|
||||||
leftInt, lok = leftValue.(int64)
|
|
||||||
rightInt, rok = rightValue.(int64)
|
|
||||||
|
|
||||||
if lok && rok {
|
|
||||||
v = leftInt & rightInt
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBitwiseAnd(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- 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 bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
|
||||||
var leftInt, rightInt int64
|
|
||||||
var lok, rok bool
|
|
||||||
|
|
||||||
leftInt, lok = leftValue.(int64)
|
|
||||||
rightInt, rok = rightValue.(int64)
|
|
||||||
|
|
||||||
if lok && rok {
|
|
||||||
v = leftInt | rightInt
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBitwiseOr(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- 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 bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
|
||||||
var leftInt, rightInt int64
|
|
||||||
var lok, rok bool
|
|
||||||
|
|
||||||
leftInt, lok = leftValue.(int64)
|
|
||||||
rightInt, rok = rightValue.(int64)
|
|
||||||
|
|
||||||
if lok && rok {
|
|
||||||
v = leftInt ^ rightInt
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBitwiseXor(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
|
||||||
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
|
||||||
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
|
||||||
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
|
||||||
}
|
|
||||||
@ -4,11 +4,7 @@
|
|||||||
// operator-bool.go
|
// operator-bool.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- NOT term
|
//-------- NOT term
|
||||||
|
|
||||||
@ -22,17 +18,17 @@ func newNotTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalNot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalNot(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var rightValue any
|
var rightValue any
|
||||||
|
|
||||||
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, ok := kern.ToBool(rightValue); ok {
|
if b, ok := toBool(rightValue); ok {
|
||||||
v = !b
|
v = !b
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(rightValue)
|
err = self.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -42,6 +38,8 @@ func evalNot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|||||||
func newAndTerm(tk *Token) (inst *term) {
|
func newAndTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
|
// class: classOperator,
|
||||||
|
// kind: kindBool,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priAnd,
|
priority: priAnd,
|
||||||
@ -49,8 +47,8 @@ func newAndTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAnd(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
if isEnabled(ctx, ControlBoolShortcut) {
|
||||||
v, err = evalAndWithShortcut(ctx, self)
|
v, err = evalAndWithShortcut(ctx, self)
|
||||||
} else {
|
} else {
|
||||||
v, err = evalAndWithoutShortcut(ctx, self)
|
v, err = evalAndWithoutShortcut(ctx, self)
|
||||||
@ -58,7 +56,7 @@ func evalAnd(ctx kern.ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAndWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
var leftBool, rightBool bool
|
var leftBool, rightBool bool
|
||||||
var lok, rok bool
|
var lok, rok bool
|
||||||
@ -67,8 +65,8 @@ func evalAndWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = kern.ToBool(leftValue)
|
leftBool, lok = toBool(leftValue)
|
||||||
rightBool, rok = kern.ToBool(rightValue)
|
rightBool, rok = toBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool && rightBool
|
v = leftBool && rightBool
|
||||||
@ -78,25 +76,24 @@ func evalAndWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAndWithShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
if err = self.checkOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftValue, err = self.children[0].Compute(ctx); err != nil {
|
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
if leftBool, lok := toBool(leftValue); !lok {
|
||||||
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
|
err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool)
|
||||||
// return
|
return
|
||||||
err = self.errIncompatibleType(leftValue, "left")
|
|
||||||
} else if !leftBool {
|
} else if !leftBool {
|
||||||
v = false
|
v = false
|
||||||
} else if rightValue, err = self.children[1].Compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
if rightBool, rok := toBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
@ -110,6 +107,8 @@ func evalAndWithShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
|
|||||||
func newOrTerm(tk *Token) (inst *term) {
|
func newOrTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
|
// class: classOperator,
|
||||||
|
// kind: kindBool,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priOr,
|
priority: priOr,
|
||||||
@ -117,8 +116,8 @@ func newOrTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalOr(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalOr(ctx ExprContext, self *term) (v any, err error) {
|
||||||
if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
|
if isEnabled(ctx, ControlBoolShortcut) {
|
||||||
v, err = evalOrWithShortcut(ctx, self)
|
v, err = evalOrWithShortcut(ctx, self)
|
||||||
} else {
|
} else {
|
||||||
v, err = evalOrWithoutShortcut(ctx, self)
|
v, err = evalOrWithoutShortcut(ctx, self)
|
||||||
@ -126,7 +125,7 @@ func evalOr(ctx kern.ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalOrWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
var leftBool, rightBool bool
|
var leftBool, rightBool bool
|
||||||
var lok, rok bool
|
var lok, rok bool
|
||||||
@ -135,8 +134,8 @@ func evalOrWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = kern.ToBool(leftValue)
|
leftBool, lok = toBool(leftValue)
|
||||||
rightBool, rok = kern.ToBool(rightValue)
|
rightBool, rok = toBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool || rightBool
|
v = leftBool || rightBool
|
||||||
@ -146,24 +145,24 @@ func evalOrWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalOrWithShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
|
func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
if err = self.checkOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftValue, err = self.children[0].Compute(ctx); err != nil {
|
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
if leftBool, lok := toBool(leftValue); !lok {
|
||||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
|
err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool)
|
||||||
return
|
return
|
||||||
} else if leftBool {
|
} else if leftBool {
|
||||||
v = true
|
v = true
|
||||||
} else if rightValue, err = self.children[1].Compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
if rightBool, rok := toBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
|||||||
@ -1,15 +1,9 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// operator-builtin.go
|
// operator-length.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- builtin term
|
//-------- builtin term
|
||||||
|
|
||||||
func newBuiltinTerm(tk *Token) (inst *term) {
|
func newBuiltinTerm(tk *Token) (inst *term) {
|
||||||
@ -22,42 +16,37 @@ func newBuiltinTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalBuiltin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var childValue any
|
var rightValue any
|
||||||
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if kern.IsString(childValue) {
|
if isList(rightValue) {
|
||||||
module, _ := childValue.(string)
|
list, _ := rightValue.([]any)
|
||||||
count, err = ImportInContextByGlobPattern(ctx, module)
|
for i, moduleSpec := range list {
|
||||||
} else {
|
|
||||||
var moduleSpec any
|
|
||||||
var it kern.Iterator
|
|
||||||
if it, err = NewIterator(childValue); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
|
||||||
if module, ok := moduleSpec.(string); ok {
|
if module, ok := moduleSpec.(string); ok {
|
||||||
if ImportInContext(ctx, module) {
|
if ImportInContext(ctx, module) {
|
||||||
count++
|
count++
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.Errorf("unknown builtin module %q", module)
|
err = self.Errorf("unknown module %q", module)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, kern.TypeName(moduleSpec))
|
err = self.Errorf("expected string at item nr %d, got %T", i+1, moduleSpec)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
} else if isString(rightValue) {
|
||||||
err = nil
|
module, _ := rightValue.(string)
|
||||||
}
|
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v = int64(count)
|
v = count
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,6 @@
|
|||||||
// operator-but.go
|
// operator-but.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- but term
|
//-------- but term
|
||||||
|
|
||||||
func newButTerm(tk *Token) (inst *term) {
|
func newButTerm(tk *Token) (inst *term) {
|
||||||
@ -20,8 +16,8 @@ func newButTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalBut(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalBut(ctx ExprContext, self *term) (v any, err error) {
|
||||||
_, v, err = opTerm.evalInfix(ctx)
|
_, v, err = self.evalInfix(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
93
operator-coalesce.go
Normal file
93
operator-coalesce.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-coalesce.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- null coalesce term
|
||||||
|
|
||||||
|
func newNullCoalesceTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
// class: classOperator,
|
||||||
|
// kind: kindUnknown,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priCoalesce,
|
||||||
|
evalFunc: evalNullCoalesce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var rightValue any
|
||||||
|
|
||||||
|
if err = self.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := self.children[0]
|
||||||
|
if leftTerm.tk.Sym != SymIdentifier {
|
||||||
|
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
|
v = leftValue
|
||||||
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
if _, ok := rightValue.(Functor); ok {
|
||||||
|
err = errCoalesceNoFunc(self.children[1])
|
||||||
|
} else {
|
||||||
|
v = rightValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- coalesce assign term
|
||||||
|
|
||||||
|
func newCoalesceAssignTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priCoalesce,
|
||||||
|
evalFunc: evalAssignCoalesce,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var rightValue any
|
||||||
|
|
||||||
|
if err = self.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := self.children[0]
|
||||||
|
if leftTerm.tk.Sym != SymIdentifier {
|
||||||
|
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
|
v = leftValue
|
||||||
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
if _, ok := rightValue.(Functor); ok {
|
||||||
|
err = errCoalesceNoFunc(self.children[1])
|
||||||
|
} else {
|
||||||
|
v = rightValue
|
||||||
|
ctx.setVar(leftTerm.source(), rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// utils
|
||||||
|
func errCoalesceNoFunc(t *term) error {
|
||||||
|
return t.Errorf("the right operand of a coalescing operation cannot be a function definition")
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
|
||||||
|
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
|
||||||
|
}
|
||||||
@ -1,64 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-context-value.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- context term
|
|
||||||
|
|
||||||
func newContextTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priIncDec,
|
|
||||||
evalFunc: evalContextValue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalContextValue(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
|
|
||||||
var sourceCtx kern.ExprContext
|
|
||||||
if len(opTerm.children) == 0 {
|
|
||||||
sourceCtx = ctx
|
|
||||||
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].Source() == "global" {
|
|
||||||
sourceCtx = ctx.GetGlobal()
|
|
||||||
} else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
|
|
||||||
if dc, ok := childValue.(*dataCursor); ok {
|
|
||||||
sourceCtx = dc.ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sourceCtx != nil {
|
|
||||||
if formatter, ok := sourceCtx.(kern.DictFormat); ok {
|
|
||||||
v = formatter.ToDict()
|
|
||||||
} else if formatter, ok := sourceCtx.(kern.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)
|
|
||||||
}
|
|
||||||
keys = sourceCtx.EnumFuncs(func(name string) bool { return true })
|
|
||||||
for _, key := range keys {
|
|
||||||
d[key], _ = sourceCtx.GetFuncInfo(key)
|
|
||||||
}
|
|
||||||
v = d
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoubleDollar, newContextTerm)
|
|
||||||
}
|
|
||||||
@ -4,10 +4,6 @@
|
|||||||
// operator-ctrl.go
|
// operator-ctrl.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- export all term
|
//-------- export all term
|
||||||
|
|
||||||
func newExportAllTerm(tk *Token) (inst *term) {
|
func newExportAllTerm(tk *Token) (inst *term) {
|
||||||
@ -20,8 +16,8 @@ func newExportAllTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalExportAll(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalExportAll(ctx ExprContext, self *term) (v any, err error) {
|
||||||
CtrlEnable(ctx, kern.ControlExportAll)
|
enable(ctx, control_export_all)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,128 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-default.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- default term
|
|
||||||
|
|
||||||
func newDefaultTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priDefault,
|
|
||||||
evalFunc: evalDefault,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalDefault(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var rightValue any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := opTerm.children[0]
|
|
||||||
if leftTerm.tk.Sym != SymVariable {
|
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
|
||||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
|
|
||||||
v = leftValue
|
|
||||||
} else if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
v = rightValue
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- alternate term
|
|
||||||
|
|
||||||
func newAlternateTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priDefault,
|
|
||||||
evalFunc: evalAlternate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalAlternate(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var rightValue any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := opTerm.children[0]
|
|
||||||
if leftTerm.tk.Sym != SymVariable {
|
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
|
||||||
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 {
|
|
||||||
v = rightValue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v = leftValue
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- 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 evalAssignDefault(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var rightValue any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftTerm := opTerm.children[0]
|
|
||||||
if leftTerm.tk.Sym != SymVariable {
|
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
|
||||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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.(kern.Functor); ok {
|
|
||||||
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
|
||||||
ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
|
|
||||||
NewFuncParamFlag(kern.ParamValue, PfDefault|PfRepeat),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
v = rightValue
|
|
||||||
ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
|
|
||||||
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
|
|
||||||
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-digest.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- digest term
|
|
||||||
|
|
||||||
func newDigestTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priIterOp,
|
|
||||||
evalFunc: evalDigest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalDigest(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var it kern.Iterator
|
|
||||||
var item, lastValue any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if it, err = NewIterator(leftValue); err != nil {
|
|
||||||
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
lastValue = nil
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
ctx.SetVar("_", item)
|
|
||||||
ctx.SetVar("__", it.Index())
|
|
||||||
ctx.SetVar("_#", it.Count())
|
|
||||||
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
if rightValue == nil {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
lastValue = rightValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
|
||||||
ctx.DeleteVar("_")
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
v = lastValue
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwDigest, newDigestTerm)
|
|
||||||
}
|
|
||||||
@ -4,10 +4,6 @@
|
|||||||
// operator-dot.go
|
// operator-dot.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
func newDotTerm(tk *Token) (inst *term) {
|
func newDotTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
@ -19,51 +15,42 @@ func newDotTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
return
|
|
||||||
}
|
|
||||||
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := opTerm.children[1]
|
indexTerm := self.children[1]
|
||||||
|
if !isInteger(rightValue) {
|
||||||
|
err = indexTerm.Errorf("index expression must be integer, got %T", rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
index64, _ := rightValue.(int64)
|
||||||
case kern.ExtIterator:
|
index := int(index64)
|
||||||
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
|
|
||||||
opName := indexTerm.Source()
|
if isList(leftValue) {
|
||||||
if unboxedValue.HasOperation(opName) {
|
list, _ := leftValue.([]any)
|
||||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
if index >= 0 && index < len(list) {
|
||||||
|
v = list[index]
|
||||||
|
} else if index >= -len(list) {
|
||||||
|
v = list[len(list)+index]
|
||||||
} else {
|
} else {
|
||||||
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
v = false
|
}
|
||||||
|
} else if isString(leftValue) {
|
||||||
|
s, _ := leftValue.(string)
|
||||||
|
if index >= 0 && index < len(s) {
|
||||||
|
v = string(s[index])
|
||||||
|
} else if index >= -len(s) {
|
||||||
|
v = string(s[len(s)+index])
|
||||||
|
} else {
|
||||||
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = indexTerm.tk.ErrorExpectedGot("identifier")
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
|
||||||
case *kern.DictType:
|
|
||||||
var ok bool
|
|
||||||
s := opTerm.children[1].symbol()
|
|
||||||
if s == SymVariable || s == 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.errKeyNotFound(src)
|
|
||||||
}
|
|
||||||
} else if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
|
||||||
err = opTerm.errKeyNotFound(rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,11 +4,7 @@
|
|||||||
// operator-fact.go
|
// operator-fact.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- fact term
|
//-------- fact term
|
||||||
|
|
||||||
@ -22,14 +18,14 @@ func newFactTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalFact(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalFact(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue any
|
var leftValue any
|
||||||
|
|
||||||
if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
|
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger(leftValue) {
|
if isInteger(leftValue) {
|
||||||
if i, _ := leftValue.(int64); i >= 0 {
|
if i, _ := leftValue.(int64); i >= 0 {
|
||||||
f := int64(1)
|
f := int64(1)
|
||||||
for k := int64(1); k <= i; k++ {
|
for k := int64(1); k <= i; k++ {
|
||||||
@ -40,7 +36,7 @@ func evalFact(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|||||||
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
|
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(leftValue)
|
err = self.errIncompatibleType(leftValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,74 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-filter.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- map term
|
|
||||||
|
|
||||||
func newFilterTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priIterOp,
|
|
||||||
evalFunc: evalFilter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalFilter(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var it kern.Iterator
|
|
||||||
var item any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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 FILTER must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values := kern.NewListA()
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
ctx.SetVar("_", item)
|
|
||||||
ctx.SetVar("__", it.Index())
|
|
||||||
ctx.SetVar("_#", it.Count())
|
|
||||||
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
if success, valid := kern.ToBool(rightValue); valid {
|
|
||||||
if success {
|
|
||||||
values.AppendItem(item)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
|
||||||
ctx.DeleteVar("_")
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
v = values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwFilter, newFilterTerm)
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operand-fraction.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- fraction term
|
|
||||||
func newFractionTerm(tk *Token) *term {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
parent: nil,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priFraction,
|
|
||||||
evalFunc: evalFraction,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- eval func
|
|
||||||
func evalFraction(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var numValue, denValue any
|
|
||||||
var num, den int64
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if num, ok = numValue.(int64); !ok {
|
|
||||||
err = fmt.Errorf("numerator must be integer, got %T (%v)", numValue, numValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if den, ok = denValue.(int64); !ok {
|
|
||||||
err = fmt.Errorf("denominator must be integer, got %T (%v)", denValue, denValue)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if den == 0 {
|
|
||||||
err = opTerm.errDivisionByZero()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if den < 0 {
|
|
||||||
den = -den
|
|
||||||
num = -num
|
|
||||||
}
|
|
||||||
if num != 0 {
|
|
||||||
if g := kern.Gcd(num, den); g != 1 {
|
|
||||||
num = num / g
|
|
||||||
den = den / g
|
|
||||||
}
|
|
||||||
if den == 1 {
|
|
||||||
v = num
|
|
||||||
} else {
|
|
||||||
// v = &expr.FractionType{num, den}
|
|
||||||
v = kern.NewFraction(num, den)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// v = &FractionType{0, den}
|
|
||||||
v = kern.NewFraction(0, den)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
// registerTermConstructor(SymVertBar, newFractionTerm)
|
|
||||||
registerTermConstructor(SymColon, newFractionTerm)
|
|
||||||
}
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-groupby.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- group by term
|
|
||||||
|
|
||||||
func newGroupByTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priIterOp,
|
|
||||||
evalFunc: evalGroupBy,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalGroupBy(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var it kern.Iterator
|
|
||||||
var item any
|
|
||||||
var sKey string
|
|
||||||
var keyByIndex bool
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if it, err = NewIterator(leftValue); err != nil {
|
|
||||||
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
rightTk := opTerm.children[1].tk
|
|
||||||
if rightTk.IsSymbol(SymVariable) && rightTk.source == "__" {
|
|
||||||
keyByIndex = true
|
|
||||||
} else if rightValue, err = opTerm.children[1].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
} else if kern.IsString(rightValue) {
|
|
||||||
sKey = rightValue.(string)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values := kern.MakeDict()
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
ctx.SetVar("_", item)
|
|
||||||
ctx.SetVar("__", it.Index())
|
|
||||||
ctx.SetVar("_#", it.Count())
|
|
||||||
|
|
||||||
var sItemKey string
|
|
||||||
|
|
||||||
if d, ok := item.(*kern.DictType); ok {
|
|
||||||
if keyByIndex || len(sKey) == 0 {
|
|
||||||
sItemKey = strconv.Itoa(int(it.Index()))
|
|
||||||
} else if d.HasKey(sKey) {
|
|
||||||
if keyValue, exists := d.GetItem(sKey); exists {
|
|
||||||
sItemKey = fmt.Sprintf("%v", keyValue)
|
|
||||||
} else {
|
|
||||||
sItemKey = "_"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sItemKey = "_"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sItemKey = strconv.Itoa(int(it.Index()))
|
|
||||||
}
|
|
||||||
|
|
||||||
var ls *kern.ListType
|
|
||||||
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
|
|
||||||
ls = lsAny.(*kern.ListType)
|
|
||||||
}
|
|
||||||
if ls == nil {
|
|
||||||
ls = kern.NewListA()
|
|
||||||
}
|
|
||||||
ls.AppendItem(item)
|
|
||||||
values.SetItem(sItemKey, ls)
|
|
||||||
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
|
||||||
ctx.DeleteVar("_")
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
v = values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwGroupBy, newGroupByTerm)
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-in.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- in term
|
|
||||||
|
|
||||||
func newInTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priRelational,
|
|
||||||
evalFunc: evalIn,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func hasKey(d map[any]any, target any) (ok bool) {
|
|
||||||
// _, ok = d[target]
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func evalIn(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if kern.IsList(rightValue) {
|
|
||||||
list, _ := rightValue.(*kern.ListType)
|
|
||||||
v = list.IndexDeepSameCmp(leftValue) >= 0
|
|
||||||
} else if kern.IsDict(rightValue) {
|
|
||||||
dict, _ := rightValue.(*kern.DictType)
|
|
||||||
v = dict.HasKey(leftValue)
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwIn, newInTerm)
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-include.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- include term
|
|
||||||
|
|
||||||
func newIncludeTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priSign,
|
|
||||||
evalFunc: evalInclude,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalInclude(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
if kern.IsList(childValue) {
|
|
||||||
list, _ := childValue.(*kern.ListType)
|
|
||||||
for i, filePathSpec := range *list {
|
|
||||||
if filePath, ok := filePathSpec.(string); ok {
|
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
|
||||||
count++
|
|
||||||
} else {
|
|
||||||
err = opTerm.Errorf("can't load file %q", filePath)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if kern.IsString(childValue) {
|
|
||||||
filePath, _ := childValue.(string)
|
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
//v = count
|
|
||||||
v = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwInclude, newIncludeTerm)
|
|
||||||
}
|
|
||||||
@ -1,142 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-index.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- index term
|
|
||||||
func newIndexTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priDot,
|
|
||||||
evalFunc: evalIndex,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyKey(indexList *kern.ListType) (index any, err error) {
|
|
||||||
index = (*indexList)[0]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyIndex(indexTerm *term, indexList *kern.ListType, maxValue int) (index int, err error) {
|
|
||||||
var v int
|
|
||||||
|
|
||||||
if v, err = kern.ToGoInt((*indexList)[0], "index expression"); err == nil {
|
|
||||||
if v < 0 && v >= -maxValue {
|
|
||||||
v = maxValue + v
|
|
||||||
}
|
|
||||||
if v >= 0 && v < maxValue {
|
|
||||||
index = v
|
|
||||||
} else {
|
|
||||||
err = indexTerm.Errorf("index %d out of bounds", v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyRange(indexTerm *term, indexList *kern.ListType, maxValue int) (startIndex, endIndex int, err error) {
|
|
||||||
v, _ := ((*indexList)[0]).(*intPair)
|
|
||||||
startIndex = v.a
|
|
||||||
endIndex = v.b
|
|
||||||
if endIndex == kern.ConstLastIndex {
|
|
||||||
endIndex = maxValue
|
|
||||||
}
|
|
||||||
if startIndex < 0 && startIndex >= -maxValue {
|
|
||||||
startIndex = maxValue + startIndex
|
|
||||||
}
|
|
||||||
if endIndex < 0 && endIndex >= -maxValue {
|
|
||||||
endIndex = maxValue + endIndex
|
|
||||||
}
|
|
||||||
if startIndex < 0 || startIndex > maxValue {
|
|
||||||
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex)
|
|
||||||
} else if endIndex < 0 || endIndex > maxValue {
|
|
||||||
err = indexTerm.Errorf("range end-index %d is out of bounds", endIndex)
|
|
||||||
} else if startIndex > endIndex {
|
|
||||||
err = indexTerm.Errorf("range start-index %d must not be greater than end-index %d", startIndex, endIndex)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalIndex(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var indexList *kern.ListType
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
indexTerm := opTerm.children[1]
|
|
||||||
if indexList, ok = rightValue.(*kern.ListType); !ok {
|
|
||||||
err = opTerm.Errorf("invalid index expression")
|
|
||||||
return
|
|
||||||
} else if len(*indexList) != 1 {
|
|
||||||
err = indexTerm.Errorf("one index only is allowed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if kern.IsInteger((*indexList)[0]) {
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
|
||||||
case *kern.ListType:
|
|
||||||
var index int
|
|
||||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
|
||||||
v = (*unboxedValue)[index]
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var index int
|
|
||||||
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
||||||
v = string(unboxedValue[index])
|
|
||||||
}
|
|
||||||
case *kern.DictType:
|
|
||||||
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
|
||||||
default:
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
} else if isIntPair((*indexList)[0]) {
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
|
||||||
case *kern.ListType:
|
|
||||||
var start, end int
|
|
||||||
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
|
||||||
sublist := kern.ListType((*unboxedValue)[start:end])
|
|
||||||
v = &sublist
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var start, end int
|
|
||||||
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
||||||
v = unboxedValue[start:end]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
} else if kern.IsDict(leftValue) {
|
|
||||||
d := leftValue.(*kern.DictType)
|
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
|
||||||
} else {
|
|
||||||
rightChild := opTerm.children[1]
|
|
||||||
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDictItem(d *kern.DictType, indexTerm *term, indexList *kern.ListType, rightValue any) (v any, err error) {
|
|
||||||
var ok bool
|
|
||||||
var indexValue any
|
|
||||||
|
|
||||||
if indexValue, err = verifyKey(indexList); err == nil {
|
|
||||||
if v, ok = (*d)[indexValue]; !ok {
|
|
||||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymIndex, newIndexTerm)
|
|
||||||
}
|
|
||||||
@ -4,19 +4,15 @@
|
|||||||
// operator-insert.go
|
// operator-insert.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
//-------- insert term
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- prepend term
|
func newInsertTerm(tk *Token) (inst *term) {
|
||||||
|
|
||||||
func newPrependTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priInsert,
|
priority: priAssign,
|
||||||
evalFunc: evalPrepend,
|
evalFunc: evalInsert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,71 +21,45 @@ func newAppendTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priInsert,
|
priority: priAssign,
|
||||||
evalFunc: evalAppend,
|
evalFunc: evalAppend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPrepend(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalInsert(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(rightValue) {
|
if isList(rightValue) {
|
||||||
list, _ := rightValue.(*kern.ListType)
|
list, _ := rightValue.([]any)
|
||||||
newList := append(kern.ListType{leftValue}, *list...)
|
v = append([]any{leftValue}, list...)
|
||||||
v = &newList
|
|
||||||
if opTerm.children[1].symbol() == SymVariable {
|
|
||||||
ctx.UnsafeSetVar(opTerm.children[1].Source(), v)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAppend(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalAppend(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(leftValue) {
|
if isList(leftValue) {
|
||||||
list, _ := leftValue.(*kern.ListType)
|
list, _ := leftValue.([]any)
|
||||||
newList := append(*list, rightValue)
|
v = append(list, rightValue)
|
||||||
v = &newList
|
|
||||||
if opTerm.children[0].symbol() == SymVariable {
|
|
||||||
ctx.UnsafeSetVar(opTerm.children[0].Source(), v)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
// var leftValue, rightValue any
|
|
||||||
|
|
||||||
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if IsList(leftValue) {
|
|
||||||
// list, _ := leftValue.(*ListType)
|
|
||||||
// newList := append(*list, rightValue)
|
|
||||||
// v = &newList
|
|
||||||
// if
|
|
||||||
// } else {
|
|
||||||
// err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymPlusGreater, newPrependTerm)
|
registerTermConstructor(SymInsert, newInsertTerm)
|
||||||
registerTermConstructor(SymLessPlus, newAppendTerm)
|
registerTermConstructor(SymAppend, newAppendTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-iter-value.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- iter value term
|
|
||||||
|
|
||||||
func newIterValueTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priDereference,
|
|
||||||
evalFunc: evalIterValue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalIterValue(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if it, ok := childValue.(kern.Iterator); ok {
|
|
||||||
v, err = it.Current()
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
|
||||||
registerTermConstructor(SymDereference, newIterValueTerm)
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-join.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- join term
|
|
||||||
|
|
||||||
func newJoinTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priIterOp,
|
|
||||||
evalFunc: evalJoin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalJoin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var itLeft, itRight kern.Iterator
|
|
||||||
var item any
|
|
||||||
|
|
||||||
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, err = NewIterator(leftValue); err != nil {
|
|
||||||
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
if itRight, err = NewIterator(rightValue); err != nil {
|
|
||||||
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values := kern.NewListA()
|
|
||||||
for _, it := range []kern.Iterator{itLeft, itRight} {
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
values.AppendItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
v = values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwJoin, newJoinTerm)
|
|
||||||
}
|
|
||||||
@ -4,10 +4,6 @@
|
|||||||
// operator-length.go
|
// operator-length.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- length term
|
//-------- length term
|
||||||
|
|
||||||
func newLengthTerm(tk *Token) (inst *term) {
|
func newLengthTerm(tk *Token) (inst *term) {
|
||||||
@ -20,32 +16,24 @@ func newLengthTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalLength(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var childValue any
|
var rightValue any
|
||||||
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(childValue) {
|
if isList(rightValue) {
|
||||||
ls, _ := childValue.(*kern.ListType)
|
list, _ := rightValue.([]any)
|
||||||
v = int64(len(*ls))
|
v = len(list)
|
||||||
} else if kern.IsString(childValue) {
|
} else if isString(rightValue) {
|
||||||
s, _ := childValue.(string)
|
s, _ := rightValue.(string)
|
||||||
v = int64(len(s))
|
v = len(s)
|
||||||
} else if kern.IsDict(childValue) {
|
|
||||||
m, _ := childValue.(*kern.DictType)
|
|
||||||
v = int64(len(*m))
|
|
||||||
} else if it, ok := childValue.(kern.Iterator); ok {
|
|
||||||
v = int64(it.Count())
|
|
||||||
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
|
||||||
// count, _ := extIt.CallOperation(CountName, nil)
|
|
||||||
// v, _ = ToGoInt(count, "")
|
|
||||||
// } else {
|
// } else {
|
||||||
// v = int64(it.Index() + 1)
|
// v = 1
|
||||||
// }
|
// }
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
err = self.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,70 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-map.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- map term
|
|
||||||
|
|
||||||
func newMapTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priIterOp,
|
|
||||||
evalFunc: evalMap,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalMap(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var it kern.Iterator
|
|
||||||
var item any
|
|
||||||
|
|
||||||
if err = opTerm.checkOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if it, err = NewIterator(leftValue); err != nil {
|
|
||||||
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values := kern.NewListA()
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
ctx.SetVar("_", item)
|
|
||||||
ctx.SetVar("__", it.Index())
|
|
||||||
ctx.SetVar("_#", it.Count())
|
|
||||||
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
|
|
||||||
values.AppendItem(rightValue)
|
|
||||||
}
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
|
||||||
ctx.DeleteVar("_")
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
v = values
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwMap, newMapTerm)
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-plugin.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- plugin term
|
|
||||||
|
|
||||||
func newPluginTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priSign,
|
|
||||||
evalFunc: evalPlugin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalPlugin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
var count int
|
|
||||||
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if count, err = importPluginFromSearchPath(ctx, childValue); err == nil {
|
|
||||||
v = int64(count)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymKwPlugin, newPluginTerm)
|
|
||||||
}
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-post-inc-dec.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- post increment term
|
|
||||||
|
|
||||||
func newPostIncTerm(tk *Token) *term {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
parent: nil,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPostfix,
|
|
||||||
priority: priIncDec,
|
|
||||||
evalFunc: evalPostInc,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalPostInc(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if it, ok := childValue.(kern.Iterator); ok {
|
|
||||||
var namePrefix string
|
|
||||||
v, err = it.Next()
|
|
||||||
|
|
||||||
if opTerm.children[0].symbol() == SymVariable {
|
|
||||||
namePrefix = opTerm.children[0].Source()
|
|
||||||
}
|
|
||||||
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
|
|
||||||
if c, err1 := it.Current(); err1 == nil {
|
|
||||||
ctx.UnsafeSetVar(namePrefix+"_current", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
if it.HasOperation(kern.KeyName) {
|
|
||||||
if k, err1 := it.CallOperation(kern.KeyName, nil); err1 == nil {
|
|
||||||
ctx.UnsafeSetVar(namePrefix+"_key", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if it.HasOperation(kern.ValueName) {
|
|
||||||
if v1, err1 := it.CallOperation(kern.ValueName, nil); err1 == nil {
|
|
||||||
ctx.UnsafeSetVar(namePrefix+"_value", v1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
|
||||||
v = childValue
|
|
||||||
i, _ := childValue.(int64)
|
|
||||||
ctx.SetVar(opTerm.children[0].Source(), i+1)
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- post decrement term
|
|
||||||
|
|
||||||
func newPostDecTerm(tk *Token) *term {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
parent: nil,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPostfix,
|
|
||||||
priority: priIncDec,
|
|
||||||
evalFunc: evalPostDec,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalPostDec(ctx kern.ExprContext, opTerm *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if it, ok := childValue.(Iterator); ok {
|
|
||||||
v, err = it.Next()
|
|
||||||
} else */if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
|
||||||
v = childValue
|
|
||||||
i, _ := childValue.(int64)
|
|
||||||
ctx.SetVar(opTerm.children[0].Source(), i-1)
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoublePlus, newPostIncTerm)
|
|
||||||
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user