Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dceb31f542 | |||
| 075b0b5691 | |||
| 3c51b8d2ee | |||
| a46753f453 | |||
| 9070b5c9cc | |||
| ab06702e5e | |||
| ffe1fa3aac | |||
| 1a1a475dd8 | |||
| 463e3634ba | |||
| 5f8ca47ef0 | |||
| 837b887490 | |||
| c76e1d3c8e | |||
| 315f5b22d3 | |||
| dfae593e86 | |||
| d572f3a129 | |||
| c461fd138e | |||
| 6ecbe2feb1 | |||
| 80d3c6ec7d | |||
| e09806c716 | |||
| 1a772597cb | |||
| 33b3e1fc29 | |||
| 4e3f5cfbc6 | |||
| e35d4e3f70 | |||
| b4529499d6 | |||
| 7745dc24e2 | |||
| be25385d02 | |||
| 79889cd8e1 | |||
| 234759158c | |||
| 00fda29606 | |||
| 2a2840bdf2 | |||
| 06373f5126 | |||
| e69dad5fb5 | |||
| 7459057df9 | |||
| 176969c956 | |||
| cde2efacf1 | |||
| d7a7b3218c | |||
| be3bb12f28 | |||
| 032916d4fa | |||
| f3cc0cc7ad | |||
| 905337f963 | |||
| 9a95a837f6 | |||
| 8547248ea2 | |||
| a02f998fc6 | |||
| e904003e6e | |||
| 990e04f957 | |||
| d8aed9dd7a | |||
| a73d24b171 | |||
| 3ebba83bce | |||
| 6b3bfa2a11 | |||
| 867806155e | |||
| a711333a2e | |||
| af3e946bd4 | |||
| 22a36fa630 | |||
| 6d9a379c92 | |||
| dd6404c786 | |||
| 34874ef663 | |||
| 9bc8e8ca05 | |||
| 7f367cfc49 | |||
| fe999acf2c | |||
| 2ed1a1842b | |||
| bb9493d0cc | |||
| f279bf163e | |||
| 6834d9f47b | |||
| 8051faa2bf | |||
| f30e687a79 | |||
| 2b6e46576b | |||
| dc06c03112 | |||
| e8f5d3e445 | |||
| 76ce0945f7 | |||
| 340b99bad7 | |||
| 93dac956fb | |||
| 307027d23d | |||
| 896844e772 | |||
| 9fc20077a1 | |||
| d2a4adebdd | |||
| fd8e32e12b | |||
| 1e62a51c15 | |||
| 0170baa0f5 |
@@ -8,11 +8,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Expr interface {
|
|
||||||
Eval(ctx ExprContext) (result any, err error)
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- ast
|
//-------- ast
|
||||||
|
|
||||||
type ast struct {
|
type ast struct {
|
||||||
@@ -24,65 +19,69 @@ func NewAst() *ast {
|
|||||||
return &ast{}
|
return &ast{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) ToForest() {
|
func (expr *ast) TypeName() string {
|
||||||
if self.root != nil {
|
return "Expression"
|
||||||
if self.forest == nil {
|
}
|
||||||
self.forest = make([]*term, 0)
|
|
||||||
|
func (expr *ast) ToForest() {
|
||||||
|
if expr.root != nil {
|
||||||
|
if expr.forest == nil {
|
||||||
|
expr.forest = make([]*term, 0)
|
||||||
}
|
}
|
||||||
self.forest = append(self.forest, self.root)
|
expr.forest = append(expr.forest, expr.root)
|
||||||
self.root = nil
|
expr.root = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) String() string {
|
func (expr *ast) String() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if self.root == nil {
|
if expr.root == nil {
|
||||||
sb.WriteString("(nil)")
|
sb.WriteString("(nil)")
|
||||||
} else {
|
} else {
|
||||||
self.root.toString(&sb)
|
expr.root.toString(&sb)
|
||||||
}
|
}
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) addTokens(tokens ...*Token) (err error) {
|
func (expr *ast) addTokens(tokens ...*Token) (err error) {
|
||||||
for _, tk := range tokens {
|
for _, tk := range tokens {
|
||||||
if err = self.addToken(tk); err != nil {
|
if _, err = expr.addToken(tk); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) addToken(tk *Token) (err error) {
|
// func (expr *ast) addToken(tk *Token) (err error) {
|
||||||
_, err = self.addToken2(tk)
|
// _, err = expr.addToken2(tk)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
func (expr *ast) addToken(tk *Token) (t *term, err error) {
|
||||||
if t = newTerm(tk); t != nil {
|
if t = newTerm(tk); t != nil {
|
||||||
err = self.addTerm(t)
|
err = expr.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("unexpected token %q", tk.String())
|
err = tk.Errorf("unexpected token %q", tk.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) addTerm(node *term) (err error) {
|
func (expr *ast) addTerm(node *term) (err error) {
|
||||||
if self.root == nil {
|
if expr.root == nil {
|
||||||
self.root = node
|
expr.root = node
|
||||||
} else {
|
} else {
|
||||||
self.root, err = self.insert(self.root, node)
|
expr.root, err = expr.insert(expr.root, node)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) insert(tree, node *term) (root *term, err error) {
|
func (expr *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 = self.insert(last, node); err == nil {
|
if subRoot, err = expr.insert(last, node); err == nil {
|
||||||
subRoot.setParent(tree)
|
subRoot.setParent(tree)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -97,20 +96,20 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) Finish() {
|
func (expr *ast) Finish() {
|
||||||
if self.root == nil && self.forest != nil && len(self.forest) >= 1 {
|
if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
|
||||||
self.root = self.forest[len(self.forest)-1]
|
expr.root = expr.forest[len(expr.forest)-1]
|
||||||
self.forest = self.forest[0 : len(self.forest)-1]
|
expr.forest = expr.forest[0 : len(expr.forest)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) Eval(ctx ExprContext) (result any, err error) {
|
func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
|
||||||
self.Finish()
|
expr.Finish()
|
||||||
|
|
||||||
if self.root != nil {
|
if expr.root != nil {
|
||||||
// initDefaultVars(ctx)
|
// initDefaultVars(ctx)
|
||||||
if self.forest != nil {
|
if expr.forest != nil {
|
||||||
for _, root := range self.forest {
|
for _, root := range expr.forest {
|
||||||
if result, err = root.compute(ctx); err == nil {
|
if result, err = root.compute(ctx); err == nil {
|
||||||
ctx.UnsafeSetVar(ControlLastResult, result)
|
ctx.UnsafeSetVar(ControlLastResult, result)
|
||||||
} else {
|
} else {
|
||||||
@@ -120,7 +119,7 @@ func (self *ast) Eval(ctx ExprContext) (result any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if result, err = self.root.compute(ctx); err == nil {
|
if result, err = expr.root.compute(ctx); err == nil {
|
||||||
ctx.UnsafeSetVar(ControlLastResult, result)
|
ctx.UnsafeSetVar(ControlLastResult, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-19
@@ -12,35 +12,30 @@ type exprFunctor struct {
|
|||||||
defCtx ExprContext
|
defCtx ExprContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor {
|
func (functor *exprFunctor) GetParams() (params []ExprFuncParam) {
|
||||||
// return &exprFunctor{expr: e, params: params, defCtx: ctx}
|
return functor.params
|
||||||
// }
|
}
|
||||||
|
|
||||||
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
|
||||||
var defCtx ExprContext
|
var defCtx ExprContext
|
||||||
if ctx != nil {
|
if ctx != nil {
|
||||||
// if ctx.GetParent() != nil {
|
defCtx = ctx
|
||||||
// defCtx = ctx.Clone()
|
|
||||||
// defCtx.SetParent(ctx)
|
|
||||||
// } else {
|
|
||||||
defCtx = ctx
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (functor *exprFunctor) TypeName() string {
|
||||||
|
return "ExprFunctor"
|
||||||
|
}
|
||||||
|
|
||||||
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
func (functor *exprFunctor) GetDefinitionContext() ExprContext {
|
||||||
return functor.defCtx
|
return functor.defCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
func (functor *exprFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
// if functor.defCtx != nil {
|
var missing []string
|
||||||
// ctx.Merge(functor.defCtx)
|
for _, p := range functor.params {
|
||||||
// }
|
if arg, exists := args[p.Name()]; exists {
|
||||||
|
|
||||||
for i, p := range functor.params {
|
|
||||||
if i < len(args) {
|
|
||||||
arg := args[i]
|
|
||||||
if funcArg, ok := arg.(Functor); ok {
|
if funcArg, ok := arg.(Functor); ok {
|
||||||
paramSpecs := funcArg.GetParams()
|
paramSpecs := funcArg.GetParams()
|
||||||
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs)
|
||||||
@@ -48,9 +43,17 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
|
|||||||
ctx.UnsafeSetVar(p.Name(), arg)
|
ctx.UnsafeSetVar(p.Name(), arg)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.UnsafeSetVar(p.Name(), nil)
|
if missing == nil {
|
||||||
|
missing = make([]string, 0, 1)
|
||||||
|
}
|
||||||
|
missing = append(missing, p.Name())
|
||||||
|
// ctx.UnsafeSetVar(p.Name(), nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result, err = functor.expr.Eval(ctx)
|
if missing != nil {
|
||||||
|
err = ErrMissingParams(name, missing)
|
||||||
|
} else {
|
||||||
|
result, err = functor.expr.Eval(ctx)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ func NewGolangFunctor(f FuncTemplate) *golangFunctor {
|
|||||||
return &golangFunctor{f: f}
|
return &golangFunctor{f: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
|
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)
|
return functor.f(ctx, name, args)
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-39
@@ -10,53 +10,57 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
const (
|
||||||
result = args[0] == nil
|
ParamDenominator = "denominator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isNilFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
result = args[ParamValue] == nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isIntFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsInteger(args[0])
|
result = IsInteger(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isFloatFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsFloat(args[0])
|
result = IsFloat(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isBoolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsBool(args[0])
|
result = IsBool(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isStringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsString(args[0])
|
result = IsString(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isFractionFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsFract(args[0])
|
result = IsFract(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isRationalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsRational(args[0])
|
result = IsRational(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isListFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsList(args[0])
|
result = IsList(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func isDictionaryFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = IsDict(args[0])
|
result = IsDict(args[ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = (v != 0)
|
result = (v != 0)
|
||||||
case *FractionType:
|
case *FractionType:
|
||||||
@@ -73,8 +77,8 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = v
|
result = v
|
||||||
case float64:
|
case float64:
|
||||||
@@ -96,8 +100,8 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func decFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = float64(v)
|
result = float64(v)
|
||||||
case float64:
|
case float64:
|
||||||
@@ -121,8 +125,8 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func stringFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = strconv.FormatInt(v, 10)
|
result = strconv.FormatInt(v, 10)
|
||||||
case float64:
|
case float64:
|
||||||
@@ -147,18 +151,18 @@ func stringFunc(ctx ExprContext, name string, args []any) (result any, err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
switch v := args[ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
var den int64 = 1
|
var den int64 = 1
|
||||||
if len(args) > 1 {
|
|
||||||
var ok bool
|
var ok bool
|
||||||
if den, ok = args[1].(int64); !ok {
|
if den, ok = args[ParamDenominator].(int64); !ok {
|
||||||
err = ErrExpectedGot(name, "integer", args[1])
|
err = ErrExpectedGot(name, "integer", args[ParamDenominator])
|
||||||
} else if den == 0 {
|
} else if den == 0 {
|
||||||
err = ErrFuncDivisionByZero(name)
|
err = ErrFuncDivisionByZero(name)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result = newFraction(v, den)
|
result = newFraction(v, den)
|
||||||
}
|
}
|
||||||
@@ -180,9 +184,9 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
// func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
anyParams := []ExprFuncParam{
|
anyParams := []ExprFuncParam{
|
||||||
@@ -205,7 +209,7 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
|
ctx.RegisterFunc("string", NewGolangFunctor(stringFunc), TypeString, anyParams)
|
||||||
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
|
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
|
||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
NewFuncParamFlagDef("denominator", PfDefault, 1),
|
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -21,19 +21,25 @@ func getStdout(ctx ExprContext) io.Writer {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func printFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var n int
|
var n int = 0
|
||||||
if n, err = fmt.Fprint(getStdout(ctx), args...); err == nil {
|
if v, exists := args[ParamItem]; exists && v != nil {
|
||||||
result = int64(n)
|
argv := v.([]any)
|
||||||
|
n, err = fmt.Fprint(getStdout(ctx), argv...)
|
||||||
}
|
}
|
||||||
|
result = int64(n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func printLnFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var n int
|
var n int = 0
|
||||||
if n, err = fmt.Fprintln(getStdout(ctx), args...); err == nil {
|
if v, exists := args[ParamItem]; exists && v != nil {
|
||||||
result = int64(n)
|
argv := v.([]any)
|
||||||
|
n, err = fmt.Fprintln(getStdout(ctx), argv...)
|
||||||
|
} else {
|
||||||
|
n, err = fmt.Fprintln(getStdout(ctx))
|
||||||
}
|
}
|
||||||
|
result = int64(n)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -9,18 +9,21 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func importFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
return importGeneral(ctx, name, args)
|
return importGeneral(ctx, name, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func importAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
CtrlEnable(ctx, control_export_all)
|
CtrlEnable(ctx, control_export_all)
|
||||||
return importGeneral(ctx, name, args)
|
return importGeneral(ctx, name, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) {
|
func importGeneral(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
|
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH)
|
||||||
result, err = doImport(ctx, name, dirList, NewArrayIterator(args))
|
if v, exists := args[ParamFilepath]; exists && v != nil {
|
||||||
|
argv := v.([]any)
|
||||||
|
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// builtin-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
iterParamOperator = "operator"
|
||||||
|
iterParamVars = "vars"
|
||||||
|
iterVarStatus = "status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Functor, err error) {
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if it, ok = args[ParamIterator].(Iterator); !ok {
|
||||||
|
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[ParamIterator], TypeName(args[ParamIterator]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
|
||||||
|
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var vars *DictType
|
||||||
|
if vars, ok = args[iterParamVars].(*DictType); !ok && args[iterParamVars] != nil {
|
||||||
|
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], TypeName(args[iterParamVars]))
|
||||||
|
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 ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var it Iterator
|
||||||
|
var ok bool
|
||||||
|
var op 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
|
||||||
|
} else if op == nil {
|
||||||
|
op = NewGolangFunctor(printLnFunc)
|
||||||
|
usingDefaultOp = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
|
if usingDefaultOp {
|
||||||
|
params = map[string]any{ParamItem: []any{item}}
|
||||||
|
} else {
|
||||||
|
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
var success bool
|
||||||
|
if success, ok = ToBool(v); !success || !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
result, _ = localCtx.GetVar(iterVarStatus)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportIterFuncs(ctx ExprContext) {
|
||||||
|
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParam(ParamIterator),
|
||||||
|
NewFuncParamFlag(iterParamOperator, PfOptional),
|
||||||
|
NewFuncParamFlag(iterParamVars, PfOptional),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
|
||||||
|
}
|
||||||
+12
-10
@@ -34,8 +34,8 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
|
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||||
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
|
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,8 +86,9 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func addFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1)
|
argv := args[ParamValue].([]any)
|
||||||
|
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +108,8 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
|
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(CleanName) {
|
||||||
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
|
if _, err = extIter.CallOperation(CleanName, nil); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,17 +162,18 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func mulFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1)
|
argv := args[ParamValue].([]any)
|
||||||
|
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ImportMathFuncs(ctx ExprContext) {
|
func ImportMathFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{
|
ctx.RegisterFunc("add", NewGolangFunctor(addFunc), TypeNumber, []ExprFuncParam{
|
||||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
|
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{
|
ctx.RegisterFunc("mul", NewGolangFunctor(mulFunc), TypeNumber, []ExprFuncParam{
|
||||||
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
|
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-29
@@ -11,6 +11,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
osLimitCh = "limitCh"
|
||||||
|
)
|
||||||
|
|
||||||
type osHandle interface {
|
type osHandle interface {
|
||||||
getFile() *os.File
|
getFile() *os.File
|
||||||
}
|
}
|
||||||
@@ -61,8 +65,8 @@ func errInvalidFileHandle(funcName string, v any) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func createFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
var fh *os.File
|
||||||
if fh, err = os.Create(filePath); err == nil {
|
if fh, err = os.Create(filePath); err == nil {
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
@@ -73,8 +77,8 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func openFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
var fh *os.File
|
||||||
if fh, err = os.Open(filePath); err == nil {
|
if fh, err = os.Open(filePath); err == nil {
|
||||||
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
||||||
@@ -85,8 +89,8 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func appendFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[0].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
var fh *os.File
|
||||||
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
||||||
@@ -97,13 +101,13 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func closeFileFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[0].(osHandle); !ok {
|
if handle, ok = args[ParamHandle].(osHandle); !ok {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
@@ -124,18 +128,21 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func fileWriteTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[0].(osHandle); !ok {
|
if handle, ok = args[ParamHandle].(osHandle); !ok {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if w, ok := handle.(*osWriter); ok {
|
if w, ok := handle.(*osWriter); ok {
|
||||||
result, err = fmt.Fprint(w.writer, args[1:]...)
|
if v, exists := args[ParamItem]; exists {
|
||||||
|
argv := v.([]any)
|
||||||
|
result, err = fmt.Fprint(w.writer, argv...)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
invalidFileHandle = handle
|
invalidFileHandle = handle
|
||||||
}
|
}
|
||||||
@@ -147,21 +154,21 @@ func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func fileReadTextFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = nil
|
result = nil
|
||||||
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
|
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if r, ok := handle.(*osReader); ok {
|
if r, ok := handle.(*osReader); ok {
|
||||||
var limit byte = '\n'
|
var limit byte = '\n'
|
||||||
var v string
|
var v string
|
||||||
if s, ok := args[1].(string); ok && len(s) > 0 {
|
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
|
||||||
limit = s[0]
|
limit = s[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,14 +194,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func fileReadTextAllFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle osHandle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = nil
|
result = nil
|
||||||
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
|
if handle, ok = args[ParamHandle].(osHandle); !ok || args[ParamHandle] == nil {
|
||||||
invalidFileHandle = args[0]
|
invalidFileHandle = args[ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
@@ -214,34 +221,34 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ImportOsFuncs(ctx ExprContext) {
|
func ImportOsFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
|
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||||
NewFuncParam(ParamFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
|
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||||
NewFuncParam(ParamFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
|
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeFileHandle, []ExprFuncParam{
|
||||||
NewFuncParam(ParamFilepath),
|
NewFuncParam(ParamFilepath),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
|
||||||
NewFuncParam(TypeHandle),
|
NewFuncParam(ParamHandle),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
|
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
|
||||||
NewFuncParam(TypeHandle),
|
NewFuncParam(ParamHandle),
|
||||||
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
|
NewFuncParamFlagDef(ParamItem, PfDefault|PfRepeat, ""),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
|
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
|
||||||
NewFuncParam(TypeHandle),
|
NewFuncParam(ParamHandle),
|
||||||
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
|
NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||||
NewFuncParam(TypeHandle),
|
NewFuncParam(ParamHandle),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+78
-56
@@ -10,6 +10,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
strParamOther = "other"
|
||||||
|
)
|
||||||
|
|
||||||
// --- Start of function definitions
|
// --- Start of function definitions
|
||||||
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
|
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -25,52 +29,52 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil || err == io.EOF {
|
if err == io.EOF {
|
||||||
err = nil
|
err = nil
|
||||||
result = sb.String()
|
result = sb.String()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func joinStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
// if len(args) < 1 {
|
if sep, ok := args[ParamSeparator].(string); ok {
|
||||||
// return nil, errMissingRequiredParameter(name, paramSeparator)
|
if v, exists := args[ParamItem]; exists {
|
||||||
// }
|
argv := v.([]any)
|
||||||
if sep, ok := args[0].(string); ok {
|
if len(argv) == 1 {
|
||||||
if len(args) == 1 {
|
if ls, ok := argv[0].(*ListType); ok {
|
||||||
result = ""
|
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
||||||
} else if len(args) == 2 {
|
} else if it, ok := argv[0].(Iterator); ok {
|
||||||
if ls, ok := args[1].(*ListType); ok {
|
result, err = doJoinStr(name, sep, it)
|
||||||
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
} else if s, ok := argv[0].(string); ok {
|
||||||
} else if it, ok := args[1].(Iterator); ok {
|
result = s
|
||||||
result, err = doJoinStr(name, sep, it)
|
} else {
|
||||||
|
err = ErrInvalidParameterValue(name, ParamItem, v)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ErrInvalidParameterValue(name, ParamParts, args[1])
|
result, err = doJoinStr(name, sep, NewArrayIterator(argv))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0])
|
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[ParamSeparator])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func subStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var start = 0
|
var start = 0
|
||||||
var count = -1
|
var count = -1
|
||||||
var source string
|
var source string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[ParamSource].(string); !ok {
|
||||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
|
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
}
|
}
|
||||||
|
|
||||||
if start, err = ToGoInt(args[1], name+"()"); err != nil {
|
if start, err = ToGoInt(args[ParamStart], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if count, err = ToGoInt(args[2], name+"()"); err != nil {
|
if count, err = ToGoInt(args[ParamCount], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,81 +90,99 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func trimStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var source string
|
var source string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[ParamSource].(string); !ok {
|
||||||
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
|
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
}
|
}
|
||||||
result = strings.TrimSpace(source)
|
result = strings.TrimSpace(source)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func startsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var source string
|
var source, prefix string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[ParamSource].(string); !ok {
|
||||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
}
|
}
|
||||||
for i, targetSpec := range args[1:] {
|
|
||||||
if target, ok := targetSpec.(string); ok {
|
if prefix, ok = args[ParamPrefix].(string); !ok {
|
||||||
if strings.HasPrefix(source, target) {
|
return result, ErrWrongParamType(name, ParamPrefix, TypeString, args[ParamPrefix])
|
||||||
result = true
|
}
|
||||||
|
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, TypeName(targetSpec))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func endsWithStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var source string
|
var source, suffix string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = false
|
result = false
|
||||||
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[ParamSource].(string); !ok {
|
||||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
}
|
}
|
||||||
for i, targetSpec := range args[1:] {
|
|
||||||
if target, ok := targetSpec.(string); ok {
|
if suffix, ok = args[ParamSuffix].(string); !ok {
|
||||||
if strings.HasSuffix(source, target) {
|
return result, ErrWrongParamType(name, ParamSuffix, TypeString, args[ParamSuffix])
|
||||||
result = true
|
}
|
||||||
|
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, TypeName(targetSpec))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var source, sep string
|
var source, sep string
|
||||||
var count int = -1
|
var count int = -1
|
||||||
var parts []string
|
var parts []string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if source, ok = args[0].(string); !ok {
|
if source, ok = args[ParamSource].(string); !ok {
|
||||||
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
|
return result, ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
}
|
}
|
||||||
|
|
||||||
if sep, ok = args[1].(string); !ok {
|
if sep, ok = args[ParamSeparator].(string); !ok {
|
||||||
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1])
|
return nil, fmt.Errorf("separator param must be string, got %s (%v)", TypeName(args[ParamSeparator]), args[ParamSeparator])
|
||||||
}
|
}
|
||||||
|
|
||||||
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt()
|
if count64, ok := args[ParamCount].(int64); ok { // TODO replace type assertion with toInt()
|
||||||
count = int(count64)
|
count = int(count64)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2])
|
return nil, fmt.Errorf("part count must be integer, got %s (%v)", TypeName(args[ParamCount]), args[ParamCount])
|
||||||
}
|
}
|
||||||
|
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
@@ -206,13 +228,13 @@ func ImportStringFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||||
NewFuncParam(ParamSource),
|
NewFuncParam(ParamSource),
|
||||||
NewFuncParam(ParamPrefix),
|
NewFuncParam(ParamPrefix),
|
||||||
NewFuncParamFlag("other "+ParamPrefix, PfRepeat),
|
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
|
||||||
NewFuncParam(ParamSource),
|
NewFuncParam(ParamSource),
|
||||||
NewFuncParam(ParamSuffix),
|
NewFuncParam(ParamSuffix),
|
||||||
NewFuncParamFlag("other "+ParamSuffix, PfRepeat),
|
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -18,17 +18,17 @@ func NewByteSlider(size int) *ByteSlider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ByteSlider) PushEnd(b byte) {
|
func (slider *ByteSlider) PushEnd(b byte) {
|
||||||
if self.length == cap(self.buf) {
|
if slider.length == cap(slider.buf) {
|
||||||
self.length--
|
slider.length--
|
||||||
for i := 0; i < self.length; i++ {
|
for i := 0; i < slider.length; i++ {
|
||||||
self.buf[i] = self.buf[i+1]
|
slider.buf[i] = slider.buf[i+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.buf[self.length] = b
|
slider.buf[slider.length] = b
|
||||||
self.length++
|
slider.length++
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ByteSlider) Equal(target []byte) bool {
|
func (slider *ByteSlider) Equal(target []byte) bool {
|
||||||
return target != nil && bytes.Equal(self.buf, target)
|
return target != nil && bytes.Equal(slider.buf, target)
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-2
@@ -6,8 +6,13 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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) {
|
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
|
||||||
if maxArgs < 0 {
|
if maxArgs < 0 {
|
||||||
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
|
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
|
||||||
@@ -17,8 +22,8 @@ func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
|
func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
|
||||||
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
|
err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,3 +58,13 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
|
|||||||
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
||||||
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrUnknownParam(funcName, paramName string) error {
|
||||||
|
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Operator errors
|
||||||
|
|
||||||
|
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||||
|
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
|
||||||
|
}
|
||||||
|
|||||||
+6
-1
@@ -5,8 +5,10 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
ParamArgs = "args"
|
||||||
ParamCount = "count"
|
ParamCount = "count"
|
||||||
ParamItem = "item"
|
ParamItem = "item"
|
||||||
|
ParamIndex = "index"
|
||||||
ParamParts = "parts"
|
ParamParts = "parts"
|
||||||
ParamSeparator = "separator"
|
ParamSeparator = "separator"
|
||||||
ParamSource = "source"
|
ParamSource = "source"
|
||||||
@@ -19,9 +21,12 @@ const (
|
|||||||
ParamEllipsis = "..."
|
ParamEllipsis = "..."
|
||||||
ParamFilepath = "filepath"
|
ParamFilepath = "filepath"
|
||||||
ParamDirpath = "dirpath"
|
ParamDirpath = "dirpath"
|
||||||
|
ParamHandle = "handle"
|
||||||
|
ParamResource = "resource"
|
||||||
|
ParamIterator = "iterator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// to be moved in its own source file
|
// to be moved in its own source file
|
||||||
const (
|
const (
|
||||||
ConstLastIndex = 0xFFFF_FFFF
|
ConstLastIndex = 0xFFFF_FFFF
|
||||||
)
|
)
|
||||||
|
|||||||
+11
-11
@@ -5,16 +5,16 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeAny = "any"
|
TypeAny = "any"
|
||||||
TypeBoolean = "boolean"
|
TypeBoolean = "boolean"
|
||||||
TypeFloat = "float"
|
TypeFloat = "float"
|
||||||
TypeFraction = "fraction"
|
TypeFraction = "fraction"
|
||||||
TypeHandle = "handle"
|
TypeFileHandle = "file-handle"
|
||||||
TypeInt = "integer"
|
TypeInt = "integer"
|
||||||
TypeItem = "item"
|
TypeItem = "item"
|
||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
TypePair = "pair"
|
TypePair = "pair"
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
TypeListOf = "list-of-"
|
TypeListOf = "list-of-"
|
||||||
TypeListOfStrings = "list-of-strings"
|
TypeListOfStrings = "list-of-strings"
|
||||||
)
|
)
|
||||||
|
|||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// context.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
// ---- Functor interface
|
|
||||||
type Functor interface {
|
|
||||||
Invoke(ctx ExprContext, name string, args []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
|
|
||||||
ReturnType() string
|
|
||||||
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
|
|
||||||
AllocContext(parentCtx ExprContext) (ctx ExprContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----Expression Context
|
|
||||||
type ExprContext interface {
|
|
||||||
Clone() ExprContext
|
|
||||||
// Merge(ctx ExprContext)
|
|
||||||
SetParent(ctx ExprContext)
|
|
||||||
GetParent() (ctx ExprContext)
|
|
||||||
GetVar(varName string) (value any, exists bool)
|
|
||||||
GetLast() any
|
|
||||||
SetVar(varName string, value any)
|
|
||||||
UnsafeSetVar(varName string, value any)
|
|
||||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
|
||||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
|
||||||
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
|
||||||
Call(name string, args []any) (result any, err error)
|
|
||||||
RegisterFuncInfo(info ExprFunc)
|
|
||||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
|
|
||||||
}
|
|
||||||
+126
-40
@@ -5,30 +5,49 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dataCursor struct {
|
type dataCursor struct {
|
||||||
ds map[string]Functor
|
ds map[string]Functor
|
||||||
ctx ExprContext
|
ctx ExprContext
|
||||||
index int
|
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
|
||||||
resource any
|
index int
|
||||||
nextFunc Functor
|
count int
|
||||||
cleanFunc Functor
|
current any
|
||||||
resetFunc Functor
|
lastErr error
|
||||||
currentFunc Functor
|
resource any
|
||||||
|
nextFunc Functor
|
||||||
|
cleanFunc Functor
|
||||||
|
resetFunc Functor
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
|
func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
|
||||||
dc = &dataCursor{
|
dc = &dataCursor{
|
||||||
ds: ds,
|
ds: ds,
|
||||||
index: -1,
|
initState: true,
|
||||||
ctx: ctx.Clone(),
|
index: -1,
|
||||||
|
count: 0,
|
||||||
|
current: nil,
|
||||||
|
lastErr: nil,
|
||||||
|
resource: resource,
|
||||||
|
ctx: ctx.Clone(),
|
||||||
|
nextFunc: ds[NextName],
|
||||||
|
cleanFunc: ds[CleanName],
|
||||||
|
resetFunc: ds[ResetName],
|
||||||
}
|
}
|
||||||
|
//dc.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) Context() ExprContext {
|
||||||
|
return dc.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) TypeName() string {
|
||||||
|
return "DataCursor"
|
||||||
|
}
|
||||||
|
|
||||||
// func mapToString(m map[string]Functor) string {
|
// func mapToString(m map[string]Functor) string {
|
||||||
// var sb strings.Builder
|
// var sb strings.Builder
|
||||||
// sb.WriteByte('{')
|
// sb.WriteByte('{')
|
||||||
@@ -58,7 +77,7 @@ func (dc *dataCursor) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
||||||
exists = name == indexName
|
exists = name == IndexName
|
||||||
if !exists {
|
if !exists {
|
||||||
f, ok := dc.ds[name]
|
f, ok := dc.ds[name]
|
||||||
exists = ok && isFunctor(f)
|
exists = ok && isFunctor(f)
|
||||||
@@ -66,8 +85,8 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) {
|
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
|
||||||
if name == indexName {
|
if name == IndexName {
|
||||||
value = int64(dc.Index())
|
value = int64(dc.Index())
|
||||||
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
||||||
if functor == dc.cleanFunc {
|
if functor == dc.cleanFunc {
|
||||||
@@ -76,7 +95,7 @@ func (dc *dataCursor) CallOperation(name string, args []any) (value any, err err
|
|||||||
value, err = dc.Reset()
|
value, err = dc.Reset()
|
||||||
} else {
|
} else {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
value, err = functor.Invoke(ctx, name, []any{})
|
value, err = functor.InvokeNamed(ctx, name, args)
|
||||||
exportObjects(dc.ctx, ctx)
|
exportObjects(dc.ctx, ctx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -89,15 +108,20 @@ func (dc *dataCursor) Reset() (success bool, err error) {
|
|||||||
if dc.resetFunc != nil {
|
if dc.resetFunc != nil {
|
||||||
if dc.resource != nil {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil {
|
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
||||||
dc.index = -1
|
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
||||||
}
|
|
||||||
exportObjects(dc.ctx, ctx)
|
exportObjects(dc.ctx, ctx)
|
||||||
|
dc.index = -1
|
||||||
|
dc.count = 0
|
||||||
|
dc.initState = true
|
||||||
|
dc.current = nil
|
||||||
|
dc.lastErr = nil
|
||||||
|
//dc.Next()
|
||||||
} else {
|
} else {
|
||||||
err = errInvalidDataSource()
|
err = errInvalidDataSource()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = errNoOperation(resetName)
|
err = errNoOperation(ResetName)
|
||||||
}
|
}
|
||||||
success = err == nil
|
success = err == nil
|
||||||
return
|
return
|
||||||
@@ -107,48 +131,110 @@ func (dc *dataCursor) Clean() (success bool, err error) {
|
|||||||
if dc.cleanFunc != nil {
|
if dc.cleanFunc != nil {
|
||||||
if dc.resource != nil {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
_, err = dc.cleanFunc.Invoke(ctx, cleanName, []any{dc.resource})
|
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||||
dc.resource = nil
|
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
||||||
|
// dc.resource = nil
|
||||||
exportObjects(dc.ctx, ctx)
|
exportObjects(dc.ctx, ctx)
|
||||||
} else {
|
} else {
|
||||||
err = errInvalidDataSource()
|
err = errInvalidDataSource()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = errors.New("no 'clean' function defined in the data-source")
|
err = errNoOperation(CleanName)
|
||||||
}
|
}
|
||||||
success = err == nil
|
success = err == nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
||||||
ctx := cloneContext(dc.ctx)
|
dc.init()
|
||||||
if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
|
|
||||||
|
if dc.current != nil {
|
||||||
|
item = dc.current
|
||||||
|
} else {
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
}
|
}
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
|
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
|
||||||
|
var v any
|
||||||
|
var ok bool
|
||||||
|
ctx := cloneContext(dc.ctx)
|
||||||
|
|
||||||
|
actualParams := bindActualParams(filter, []any{item, dc.index})
|
||||||
|
if v, err = filter.InvokeNamed(ctx, 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 Functor, item any) (mappedItem any, err error) {
|
||||||
|
ctx := cloneContext(dc.ctx)
|
||||||
|
actualParams := bindActualParams(mapper, []any{item, dc.index})
|
||||||
|
mappedItem, err = mapper.InvokeNamed(ctx, MapName, actualParams)
|
||||||
|
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
|
||||||
if dc.resource != nil {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
filter := dc.ds[FilterName]
|
||||||
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
|
mapper := dc.ds[MapName]
|
||||||
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
|
var item any
|
||||||
if item == nil {
|
for item == nil && dc.lastErr == nil {
|
||||||
err = io.EOF
|
ctx := cloneContext(dc.ctx)
|
||||||
} else {
|
dc.index++
|
||||||
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()
|
||||||
}
|
}
|
||||||
// fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
|
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
// fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
|
|
||||||
} else {
|
} else {
|
||||||
err = errInvalidDataSource()
|
dc.lastErr = errInvalidDataSource()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Index() int {
|
func (dc *dataCursor) Index() int {
|
||||||
return dc.index
|
return dc.index - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) Count() int {
|
||||||
|
return dc.count
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,22 @@ func MakeDict() (dict *DictType) {
|
|||||||
return
|
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) {
|
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||||
|
// TODO Change wi a call to NewDict()
|
||||||
var d DictType
|
var d DictType
|
||||||
if dictAny != nil {
|
if dictAny != nil {
|
||||||
d = make(DictType, len(dictAny))
|
d = make(DictType, len(dictAny))
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// expr-context.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// ----Expression Context
|
||||||
|
type ExprContext interface {
|
||||||
|
Clone() ExprContext
|
||||||
|
SetParent(ctx ExprContext)
|
||||||
|
GetParent() (ctx ExprContext)
|
||||||
|
GetVar(varName string) (value any, exists bool)
|
||||||
|
GetLast() any
|
||||||
|
SetVar(varName string, value any)
|
||||||
|
UnsafeSetVar(varName string, value any)
|
||||||
|
|
||||||
|
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||||
|
VarCount() int
|
||||||
|
DeleteVar(varName string)
|
||||||
|
|
||||||
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
|
FuncCount() int
|
||||||
|
DeleteFunc(funcName string)
|
||||||
|
|
||||||
|
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||||
|
Call(name string, args map[string]any) (result any, err error)
|
||||||
|
RegisterFuncInfo(info ExprFunc)
|
||||||
|
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// expr-function.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// ---- 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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// expr.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// ----Expression interface
|
||||||
|
type Expr interface {
|
||||||
|
Typer
|
||||||
|
Eval(ctx ExprContext) (result any, err error)
|
||||||
|
String() string
|
||||||
|
}
|
||||||
+4
-4
@@ -50,9 +50,10 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
} else if s[0] == '+' {
|
} else if s[0] == '+' {
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
}
|
}
|
||||||
if strings.HasSuffix(s, "()") {
|
// if strings.HasSuffix(s, "()") {
|
||||||
s = s[0 : len(s)-2]
|
// s = s[0 : len(s)-2]
|
||||||
}
|
// }
|
||||||
|
s = strings.TrimSuffix(s, "()")
|
||||||
parts = strings.SplitN(s, ".", 2)
|
parts = strings.SplitN(s, ".", 2)
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||||
return
|
return
|
||||||
@@ -311,7 +312,6 @@ func divAnyFract(af1, af2 any) (quot any, err error) {
|
|||||||
if f2.num == 0 {
|
if f2.num == 0 {
|
||||||
err = errors.New("division by zero")
|
err = errors.New("division by zero")
|
||||||
return
|
return
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if f1.num == 0 || f2.den == 0 {
|
if f1.num == 0 || f2.den == 0 {
|
||||||
quot = 0
|
quot = 0
|
||||||
|
|||||||
+169
-40
@@ -6,11 +6,12 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---- Function template
|
// ---- Function template
|
||||||
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
|
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
|
||||||
|
|
||||||
// ---- Common functor definition
|
// ---- Common functor definition
|
||||||
type baseFunctor struct {
|
type baseFunctor struct {
|
||||||
@@ -112,25 +113,24 @@ type funcInfo struct {
|
|||||||
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
||||||
var minArgs = 0
|
var minArgs = 0
|
||||||
var maxArgs = 0
|
var maxArgs = 0
|
||||||
if params != nil {
|
for _, p := range params {
|
||||||
for _, p := range params {
|
if maxArgs == -1 {
|
||||||
if maxArgs == -1 {
|
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
|
||||||
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
|
}
|
||||||
}
|
if p.IsDefault() || p.IsOptional() {
|
||||||
if p.IsDefault() || p.IsOptional() {
|
maxArgs++
|
||||||
maxArgs++
|
} else if maxArgs == minArgs {
|
||||||
} else if maxArgs == minArgs {
|
minArgs++
|
||||||
minArgs++
|
maxArgs++
|
||||||
maxArgs++
|
} else {
|
||||||
} else {
|
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
|
||||||
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
|
}
|
||||||
}
|
if p.IsRepeat() {
|
||||||
if p.IsRepeat() {
|
minArgs--
|
||||||
minArgs--
|
maxArgs = -1
|
||||||
maxArgs = -1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info = &funcInfo{
|
info = &funcInfo{
|
||||||
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
|
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
|
||||||
}
|
}
|
||||||
@@ -138,10 +138,6 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
|
|||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
|
|
||||||
// return newFuncInfo("unnamed", functor, returnType, params)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (info *funcInfo) Params() []ExprFuncParam {
|
func (info *funcInfo) Params() []ExprFuncParam {
|
||||||
return info.formalParams
|
return info.formalParams
|
||||||
}
|
}
|
||||||
@@ -217,37 +213,133 @@ func (info *funcInfo) AllocContext(parentCtx ExprContext) (ctx ExprContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info *funcInfo) PrepareCall(parentCtx ExprContext, name string, varActualParams *[]any) (ctx ExprContext, err error) {
|
func (info *funcInfo) ParamSpec(paramName string) ExprFuncParam {
|
||||||
passedCount := len(*varActualParams)
|
for _, spec := range info.formalParams {
|
||||||
if info.MinArgs() > passedCount {
|
if spec.Name() == paramName {
|
||||||
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
|
return spec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initActualParams(ctx ExprContext, info ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
|
||||||
|
var varArgs []any
|
||||||
|
var varName string
|
||||||
|
|
||||||
|
namedParamsStarted := false
|
||||||
|
|
||||||
|
formalParams := info.Params()
|
||||||
|
actualParams = make(map[string]any, len(formalParams))
|
||||||
|
if callTerm == nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := passedCount; i < len(info.formalParams); i++ {
|
for i, tree := range callTerm.children {
|
||||||
p := info.formalParams[i]
|
var paramValue any
|
||||||
if !p.IsDefault() {
|
paramCtx := ctx.Clone()
|
||||||
|
if paramValue, err = tree.compute(paramCtx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if paramName, namedParam := getAssignVarName(tree); namedParam {
|
||||||
|
if info.ParamSpec(paramName) == nil {
|
||||||
|
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
actualParams[paramName] = paramValue
|
||||||
|
namedParamsStarted = true
|
||||||
|
} else if !namedParamsStarted {
|
||||||
|
if varArgs != nil {
|
||||||
|
varArgs = append(varArgs, paramValue)
|
||||||
|
} else if i < len(formalParams) {
|
||||||
|
spec := formalParams[i]
|
||||||
|
if spec.IsRepeat() {
|
||||||
|
varArgs = make([]any, 0, len(callTerm.children)-i)
|
||||||
|
varArgs = append(varArgs, paramValue)
|
||||||
|
varName = spec.Name()
|
||||||
|
} else {
|
||||||
|
actualParams[spec.Name()] = paramValue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
*varActualParams = append(*varActualParams, p.DefaultValue())
|
|
||||||
}
|
}
|
||||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varActualParams) {
|
if err == nil {
|
||||||
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varActualParams))
|
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 err == nil {
|
if passedCount < len(info.formalParams) {
|
||||||
ctx = info.AllocContext(parentCtx)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- Call a function ---
|
// ----- Call a function ---
|
||||||
|
|
||||||
func CallFunction(parentCtx ExprContext, name string, actualParams []any) (result any, err error) {
|
func getAssignVarName(t *term) (name string, ok bool) {
|
||||||
if info, exists, _ := GetFuncInfo(parentCtx, name); exists {
|
if ok = t.symbol() == SymEqual; ok {
|
||||||
var ctx ExprContext
|
name = t.children[0].source()
|
||||||
if ctx, err = info.PrepareCall(parentCtx, name, &actualParams); err == nil {
|
}
|
||||||
functor := info.Functor()
|
return
|
||||||
result, err = functor.Invoke(ctx, name, actualParams)
|
}
|
||||||
|
|
||||||
|
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm *term) (result any, err error) {
|
||||||
|
var actualParams map[string]any
|
||||||
|
if info, exists := GetFuncInfo(parentCtx, name); exists {
|
||||||
|
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
|
||||||
|
ctx := info.AllocContext(parentCtx)
|
||||||
|
if err = info.PrepareCall(name, actualParams); err == nil {
|
||||||
|
functor := info.Functor()
|
||||||
|
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
||||||
|
exportObjectsToParent(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
var actualParams map[string]any
|
||||||
|
if info, exists := GetFuncInfo(parentCtx, name); exists {
|
||||||
|
functor := info.Functor()
|
||||||
|
actualParams = bindActualParams(functor, args)
|
||||||
|
ctx := info.AllocContext(parentCtx)
|
||||||
|
if err = info.PrepareCall(name, actualParams); err == nil {
|
||||||
|
result, err = functor.InvokeNamed(ctx, name, actualParams)
|
||||||
exportObjectsToParent(ctx)
|
exportObjectsToParent(ctx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -255,3 +347,40 @@ func CallFunction(parentCtx ExprContext, name string, actualParams []any) (resul
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CallFunctionByParams(parentCtx ExprContext, name string, params 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
|
||||||
|
}
|
||||||
|
|||||||
+14
-1
@@ -55,7 +55,20 @@ func GetLocalFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool)
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
|
|
||||||
|
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
|
||||||
|
// if len(name) > 0 {
|
||||||
|
// if item, exists = GetLocalFuncInfo(ctx, name); exists {
|
||||||
|
// ownerCtx = ctx
|
||||||
|
// } else if item, exists = globalCtx.GetFuncInfo(name); exists {
|
||||||
|
// ownerCtx = globalCtx
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
item, exists, _ = GetFuncInfoAndOwner(ctx, name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFuncInfoAndOwner(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
if item, exists = GetLocalFuncInfo(ctx, name); exists {
|
if item, exists = GetLocalFuncInfo(ctx, name); exists {
|
||||||
ownerCtx = ctx
|
ownerCtx = ctx
|
||||||
|
|||||||
+14
-6
@@ -25,13 +25,13 @@ func checkStringParamExpected(funcName string, paramValue any, paramPos int) (er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func addSourceEnvImportDirs(varName string, dirList []string) []string {
|
// func addSourceEnvImportDirs(varName string, dirList []string) []string {
|
||||||
return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
|
// return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func addPluginEnvImportDirs(varName string, dirList []string) []string {
|
// func addPluginEnvImportDirs(varName string, dirList []string) []string {
|
||||||
return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
|
// return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func addEnvImportDirs(envVarName string, dirList []string) []string {
|
func addEnvImportDirs(envVarName string, dirList []string) []string {
|
||||||
if dirSpec, exists := os.LookupEnv(envVarName); exists {
|
if dirSpec, exists := os.LookupEnv(envVarName); exists {
|
||||||
@@ -75,7 +75,11 @@ func isFile(filePath string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||||
|
var err error
|
||||||
for _, dir := range dirList {
|
for _, dir := range dirList {
|
||||||
|
if dir, err = ExpandPath(dir); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
||||||
filePath = fullPath
|
filePath = fullPath
|
||||||
break
|
break
|
||||||
@@ -90,6 +94,10 @@ func isPathRelative(filePath string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
|
func makeFilepath(filename string, dirList []string) (filePath string, err error) {
|
||||||
|
if filename, err = ExpandPath(filename); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if path.IsAbs(filename) || isPathRelative(filename) {
|
if path.IsAbs(filename) || isPathRelative(filename) {
|
||||||
if isFile(filename) {
|
if isFile(filename) {
|
||||||
filePath = filename
|
filePath = filename
|
||||||
|
|||||||
+12
-8
@@ -12,25 +12,29 @@ import (
|
|||||||
// Operator names
|
// Operator names
|
||||||
|
|
||||||
const (
|
const (
|
||||||
initName = "init"
|
InitName = "init"
|
||||||
cleanName = "clean"
|
CleanName = "clean"
|
||||||
resetName = "reset"
|
ResetName = "reset"
|
||||||
nextName = "next"
|
NextName = "next"
|
||||||
currentName = "current"
|
CurrentName = "current"
|
||||||
indexName = "index"
|
IndexName = "index"
|
||||||
countName = "count"
|
CountName = "count"
|
||||||
|
FilterName = "filter"
|
||||||
|
MapName = "map"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
|
Typer
|
||||||
Next() (item any, err error) // must return io.EOF after the last item
|
Next() (item any, err error) // must return io.EOF after the last item
|
||||||
Current() (item any, err error)
|
Current() (item any, err error)
|
||||||
Index() int
|
Index() int
|
||||||
|
Count() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtIterator interface {
|
type ExtIterator interface {
|
||||||
Iterator
|
Iterator
|
||||||
HasOperation(name string) bool
|
HasOperation(name string) bool
|
||||||
CallOperation(name string, args []any) (value any, err error)
|
CallOperation(name string, args map[string]any) (value any, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errNoOperation(name string) error {
|
func errNoOperation(name string) error {
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
// iter-list.go
|
// list-iterator.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -85,18 +85,26 @@ func (it *ListIterator) String() string {
|
|||||||
return fmt.Sprintf("$(#%d)", l)
|
return fmt.Sprintf("$(#%d)", l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) TypeName() string {
|
||||||
|
return "ListIterator"
|
||||||
|
}
|
||||||
|
|
||||||
func (it *ListIterator) HasOperation(name string) bool {
|
func (it *ListIterator) HasOperation(name string) bool {
|
||||||
yes := name == resetName || name == indexName || name == countName
|
yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
|
||||||
return yes
|
return yes
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
|
func (it *ListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
switch name {
|
switch name {
|
||||||
case resetName:
|
case NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case ResetName:
|
||||||
v, err = it.Reset()
|
v, err = it.Reset()
|
||||||
case indexName:
|
case IndexName:
|
||||||
v = int64(it.Index())
|
v = int64(it.Index())
|
||||||
case countName:
|
case CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case CountName:
|
||||||
v = it.count
|
v = it.count
|
||||||
default:
|
default:
|
||||||
err = errNoOperation(name)
|
err = errNoOperation(name)
|
||||||
@@ -135,7 +143,12 @@ func (it *ListIterator) Index() int {
|
|||||||
return it.index
|
return it.index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) Count() int {
|
||||||
|
return it.count
|
||||||
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Reset() (bool, error) {
|
func (it *ListIterator) Reset() (bool, error) {
|
||||||
it.index = it.start
|
it.index = it.start - it.step
|
||||||
|
it.count = 0
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
+14
-13
@@ -26,9 +26,10 @@ func newList(listAny []any) (list *ListType) {
|
|||||||
func NewList(listAny []any) (list *ListType) {
|
func NewList(listAny []any) (list *ListType) {
|
||||||
if listAny != nil {
|
if listAny != nil {
|
||||||
ls := make(ListType, len(listAny))
|
ls := make(ListType, len(listAny))
|
||||||
for i, item := range listAny {
|
// for i, item := range listAny {
|
||||||
ls[i] = item
|
// ls[i] = item
|
||||||
}
|
// }
|
||||||
|
copy(ls, listAny)
|
||||||
list = &ls
|
list = &ls
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -105,16 +106,16 @@ func (ls *ListType) TypeName() string {
|
|||||||
return "list"
|
return "list"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list *ListType) indexDeepCmp(target any) (index int) {
|
// func (list *ListType) indexDeepCmp(target any) (index int) {
|
||||||
index = -1
|
// index = -1
|
||||||
for i, item := range *list {
|
// for i, item := range *list {
|
||||||
if reflect.DeepEqual(item, target) {
|
// if reflect.DeepEqual(item, target) {
|
||||||
index = i
|
// index = i
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (ls *ListType) contains(t *ListType) (answer bool) {
|
func (ls *ListType) contains(t *ListType) (answer bool) {
|
||||||
if len(*ls) >= len(*t) {
|
if len(*ls) >= len(*t) {
|
||||||
|
|||||||
+2
-2
@@ -18,8 +18,8 @@ func newDictTerm(args map[any]*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- dict func
|
// -------- dict func
|
||||||
func evalDict(ctx ExprContext, self *term) (v any, err error) {
|
func evalDict(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
dict, _ := self.value().(map[any]*term)
|
dict, _ := opTerm.value().(map[any]*term)
|
||||||
items := make(DictType, len(dict))
|
items := make(DictType, len(dict))
|
||||||
for key, tree := range dict {
|
for key, tree := range dict {
|
||||||
var param any
|
var param any
|
||||||
|
|||||||
+3
-3
@@ -20,11 +20,11 @@ func newExprTerm(root *term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval expr
|
// -------- eval expr
|
||||||
func evalExpr(ctx ExprContext, self *term) (v any, err error) {
|
func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
if expr, ok := self.value().(*term); ok {
|
if expr, ok := opTerm.value().(*term); ok {
|
||||||
v, err = expr.compute(ctx)
|
v, err = expr.compute(ctx)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("expression expected, got %T", self.value())
|
err = fmt.Errorf("expression expected, got %T", opTerm.value())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-17
@@ -21,20 +21,26 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) {
|
// func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
name, _ := self.tk.Value.(string)
|
// name, _ := opTerm.tk.Value.(string)
|
||||||
params := make([]any, len(self.children), len(self.children)+5)
|
// params := make([]any, len(opTerm.children), len(opTerm.children)+5)
|
||||||
for i, tree := range self.children {
|
// for i, tree := range opTerm.children {
|
||||||
var param any
|
// var param any
|
||||||
if param, err = tree.compute(ctx); err != nil {
|
// if param, err = tree.compute(ctx); err != nil {
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
params[i] = param
|
// params[i] = param
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
v, err = CallFunction(ctx, name, params)
|
// v, err = CallFunction(ctx, name, params)
|
||||||
}
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
name, _ := opTerm.tk.Value.(string)
|
||||||
|
v, err = CallFunctionByTerm(ctx, name, opTerm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,11 +57,11 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func def
|
// -------- eval func def
|
||||||
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
bodySpec := self.value()
|
bodySpec := opTerm.value()
|
||||||
if expr, ok := bodySpec.(*ast); ok {
|
if expr, ok := bodySpec.(*ast); ok {
|
||||||
paramList := make([]ExprFuncParam, 0, len(self.children))
|
paramList := make([]ExprFuncParam, 0, len(opTerm.children))
|
||||||
for _, param := range self.children {
|
for _, param := range opTerm.children {
|
||||||
var defValue any
|
var defValue any
|
||||||
flags := paramFlags(0)
|
flags := paramFlags(0)
|
||||||
if len(param.children) > 0 {
|
if len(param.children) > 0 {
|
||||||
|
|||||||
+28
-47
@@ -5,29 +5,12 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- iterator term
|
// -------- iterator term
|
||||||
|
|
||||||
func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
|
||||||
tk.Sym = SymIterator
|
|
||||||
|
|
||||||
children := make([]*term, 0, 1+len(args))
|
|
||||||
children = append(children, dsTerm)
|
|
||||||
children = append(children, args...)
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
parent: nil,
|
|
||||||
children: children,
|
|
||||||
position: posLeaf,
|
|
||||||
priority: priValue,
|
|
||||||
evalFunc: evalIterator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newIteratorTerm(tk *Token, args []*term) *term {
|
func newIteratorTerm(tk *Token, args []*term) *term {
|
||||||
tk.Sym = SymIterator
|
tk.Sym = SymIterator
|
||||||
return &term{
|
return &term{
|
||||||
@@ -42,9 +25,9 @@ func newIteratorTerm(tk *Token, args []*term) *term {
|
|||||||
|
|
||||||
// -------- eval iterator
|
// -------- eval iterator
|
||||||
|
|
||||||
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
|
||||||
values = make([]any, len(a))
|
values = make([]any, len(terms))
|
||||||
for i, t := range a {
|
for i, t := range terms {
|
||||||
var value any
|
var value any
|
||||||
if value, err = t.compute(ctx); err == nil {
|
if value, err = t.compute(ctx); err == nil {
|
||||||
values[i] = value
|
values[i] = value
|
||||||
@@ -55,26 +38,24 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) {
|
func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) {
|
||||||
if len(self.children) < 1 || self.children[0] == nil {
|
if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
|
||||||
err = self.Errorf("missing the data-source parameter")
|
err = iteratorTerm.Errorf("missing the data-source parameter")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err = self.children[0].compute(ctx)
|
value, err = iteratorTerm.children[0].compute(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) {
|
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
|
||||||
// if dictAny, ok := firstChildValue.(map[any]any); ok {
|
|
||||||
if dictAny, ok := firstChildValue.(*DictType); ok {
|
if dictAny, ok := firstChildValue.(*DictType); ok {
|
||||||
requiredFields := []string{currentName, nextName}
|
requiredFields := []string{NextName}
|
||||||
fieldsMask := 0b11
|
fieldsMask := 0b1
|
||||||
foundFields := 0
|
foundFields := 0
|
||||||
ds = make(map[string]Functor)
|
ds = make(map[string]Functor)
|
||||||
for keyAny, item := range *dictAny {
|
for keyAny, item := range *dictAny {
|
||||||
if key, ok := keyAny.(string); ok {
|
if key, ok := keyAny.(string); ok {
|
||||||
//if functor, ok := item.(*funcDefFunctor); ok {
|
|
||||||
if functor, ok := item.(Functor); ok {
|
if functor, ok := item.(Functor); ok {
|
||||||
ds[key] = functor
|
ds[key] = functor
|
||||||
if index := slices.Index(requiredFields, key); index >= 0 {
|
if index := slices.Index(requiredFields, key); index >= 0 {
|
||||||
@@ -91,57 +72,57 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
|
|||||||
missingFields = append(missingFields, field)
|
missingFields = append(missingFields, field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var firstChildValue any
|
var firstChildValue any
|
||||||
var ds map[string]Functor
|
var ds map[string]Functor
|
||||||
|
|
||||||
if firstChildValue, err = evalFirstChild(ctx, self); err != nil {
|
if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil {
|
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds != nil {
|
if ds != nil {
|
||||||
dc := newDataCursor(ctx, ds)
|
var dc *dataCursor
|
||||||
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||||
var args []any
|
var args []any
|
||||||
if len(self.children) > 1 {
|
var resource any
|
||||||
if args, err = evalTermArray(ctx, self.children[1:]); err != nil {
|
if len(opTerm.children) > 1 {
|
||||||
|
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args = []any{}
|
args = []any{}
|
||||||
}
|
}
|
||||||
|
|
||||||
initCtx := dc.ctx.Clone()
|
actualParams := bindActualParams(initFunc, args)
|
||||||
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
|
|
||||||
|
initCtx := ctx.Clone()
|
||||||
|
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
exportObjects(dc.ctx, initCtx)
|
dcCtx := ctx.Clone()
|
||||||
|
exportObjects(dcCtx, initCtx)
|
||||||
|
dc = NewDataCursor(dcCtx, ds, resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.nextFunc, _ = ds[nextName]
|
|
||||||
dc.currentFunc, _ = ds[currentName]
|
|
||||||
dc.cleanFunc, _ = ds[cleanName]
|
|
||||||
dc.resetFunc, _ = ds[resetName]
|
|
||||||
|
|
||||||
v = dc
|
v = dc
|
||||||
} else if list, ok := firstChildValue.(*ListType); ok {
|
} else if list, ok := firstChildValue.(*ListType); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSibling(ctx, self.children, nil); err == nil {
|
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
||||||
v = NewListIterator(list, args)
|
v = NewListIterator(list, args)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var list []any
|
var list []any
|
||||||
if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil {
|
if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
|
||||||
v = NewArrayIterator(list)
|
v = NewArrayIterator(list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -21,8 +21,8 @@ func newListTerm(row, col int, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- list func
|
// -------- list func
|
||||||
func evalList(ctx ExprContext, self *term) (v any, err error) {
|
func evalList(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
list, _ := self.value().([]*term)
|
list, _ := opTerm.value().([]*term)
|
||||||
items := make(ListType, len(list))
|
items := make(ListType, len(list))
|
||||||
for i, tree := range list {
|
for i, tree := range list {
|
||||||
var param any
|
var param any
|
||||||
|
|||||||
+2
-2
@@ -17,8 +17,8 @@ func newLiteralTerm(tk *Token) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalLiteral(ctx ExprContext, self *term) (v any, err error) {
|
func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
v = self.tk.Value
|
v = opTerm.tk.Value
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval selector case
|
// -------- eval selector case
|
||||||
func evalSelectorCase(ctx ExprContext, self *term) (v any, err error) {
|
func evalSelectorCase(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
if v, ok = self.value().(*selectorCase); !ok {
|
if v, ok = opTerm.value().(*selectorCase); !ok {
|
||||||
err = fmt.Errorf("selector-case expected, got %T", self.value())
|
err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -21,11 +21,11 @@ func newVarTerm(tk *Token) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
name := self.source()
|
name := opTerm.source()
|
||||||
if v, exists = GetVar(ctx, name); !exists {
|
if v, exists = GetVar(ctx, name); !exists {
|
||||||
if info, exists, _ := GetFuncInfo(ctx, name); exists {
|
if info, exists := GetFuncInfo(ctx, name); exists {
|
||||||
v = info.Functor()
|
v = info.Functor()
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("undefined variable or function %q", name)
|
err = fmt.Errorf("undefined variable or function %q", name)
|
||||||
|
|||||||
+15
-11
@@ -60,30 +60,34 @@ func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
if err = self.checkOperands(); err != nil {
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := self.children[0]
|
leftTerm := opTerm.children[0]
|
||||||
leftSym := leftTerm.symbol()
|
leftSym := leftTerm.symbol()
|
||||||
if leftSym != SymVariable && leftSym != SymIndex {
|
if leftSym != SymVariable && leftSym != SymIndex {
|
||||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", self.tk.source)
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rightChild := self.children[1]
|
rightChild := opTerm.children[1]
|
||||||
|
|
||||||
if v, err = rightChild.compute(ctx); err == nil {
|
if v, err = rightChild.compute(ctx); err == nil {
|
||||||
if functor, ok := v.(Functor); ok {
|
if functor, ok := v.(Functor); ok {
|
||||||
if info := functor.GetFunc(); info != nil {
|
if leftSym == SymVariable {
|
||||||
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
|
if info := functor.GetFunc(); info != nil {
|
||||||
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
|
||||||
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
|
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
||||||
|
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p })
|
||||||
|
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs)
|
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs)
|
||||||
|
} else {
|
||||||
|
err = opTerm.Errorf("unknown function %s()", rightChild.source())
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("unknown function %s()", rightChild.source())
|
err = assignValue(ctx, leftTerm, v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v)
|
||||||
|
|||||||
+3
-3
@@ -18,17 +18,17 @@ func newNotTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalNot(ctx ExprContext, self *term) (v any, err error) {
|
func evalNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var rightValue any
|
var rightValue any
|
||||||
|
|
||||||
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, ok := ToBool(rightValue); ok {
|
if b, ok := ToBool(rightValue); ok {
|
||||||
v = !b
|
v = !b
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(rightValue)
|
err = opTerm.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -18,10 +18,10 @@ func newBuiltinTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
if ImportInContext(module) {
|
if ImportInContext(module) {
|
||||||
count++
|
count++
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("unknown builtin module %q", module)
|
err = opTerm.Errorf("unknown builtin module %q", module)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
|
err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -16,8 +16,8 @@ func newButTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalBut(ctx ExprContext, self *term) (v any, err error) {
|
func evalBut(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
_, v, err = self.evalInfix(ctx)
|
_, v, err = opTerm.evalInfix(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
// 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,
|
|
||||||
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 != SymVariable {
|
|
||||||
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 {
|
|
||||||
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 != SymVariable {
|
|
||||||
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 functor, ok := rightValue.(Functor); ok {
|
|
||||||
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
|
||||||
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
v = rightValue
|
|
||||||
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
|
|
||||||
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
|
|
||||||
}
|
|
||||||
+5
-5
@@ -16,15 +16,15 @@ func newContextTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
|
func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
var sourceCtx ExprContext
|
var sourceCtx ExprContext
|
||||||
if self.children == nil || len(self.children) == 0 {
|
if opTerm.children == nil || len(opTerm.children) == 0 {
|
||||||
sourceCtx = ctx
|
sourceCtx = ctx
|
||||||
} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" {
|
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
||||||
sourceCtx = globalCtx
|
sourceCtx = globalCtx
|
||||||
} else if childValue, err = self.evalPrefix(ctx); err == nil {
|
} else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
|
||||||
if dc, ok := childValue.(*dataCursor); ok {
|
if dc, ok := childValue.(*dataCursor); ok {
|
||||||
sourceCtx = dc.ctx
|
sourceCtx = dc.ctx
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = d
|
v = d
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ func newExportAllTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalExportAll(ctx ExprContext, self *term) (v any, err error) {
|
func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
CtrlEnable(ctx, control_export_all)
|
CtrlEnable(ctx, control_export_all)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-default.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- 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 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 = 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 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 = 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 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 = 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.(Functor); ok {
|
||||||
|
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
v = rightValue
|
||||||
|
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
|
||||||
|
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
|
||||||
|
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
|
||||||
|
}
|
||||||
+7
-7
@@ -15,24 +15,24 @@ func newDotTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := self.children[1]
|
indexTerm := opTerm.children[1]
|
||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
case ExtIterator:
|
case ExtIterator:
|
||||||
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
|
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
|
||||||
opName := indexTerm.source()
|
opName := indexTerm.source()
|
||||||
if unboxedValue.HasOperation(opName) {
|
if unboxedValue.HasOperation(opName) {
|
||||||
v, err = unboxedValue.CallOperation(opName, []any{})
|
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||||
} else {
|
} else {
|
||||||
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
||||||
v = false
|
v = false
|
||||||
@@ -41,8 +41,8 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
err = indexTerm.tk.ErrorExpectedGot("identifier")
|
err = indexTerm.tk.ErrorExpectedGot("identifier")
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if rightValue, err = self.children[1].compute(ctx); err == nil {
|
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
+3
-3
@@ -18,10 +18,10 @@ func newFactTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalFact(ctx ExprContext, self *term) (v any, err error) {
|
func evalFact(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue any
|
var leftValue any
|
||||||
|
|
||||||
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ func evalFact(ctx ExprContext, self *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 = self.errIncompatibleType(leftValue)
|
err = opTerm.errIncompatibleType(leftValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ func newFractionTerm(tk *Token) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalFraction(ctx ExprContext, self *term) (v any, err error) {
|
func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var numValue, denValue any
|
var numValue, denValue any
|
||||||
var num, den int64
|
var num, den int64
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if numValue, denValue, err = self.evalInfix(ctx); err != nil {
|
if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if num, ok = numValue.(int64); !ok {
|
if num, ok = numValue.(int64); !ok {
|
||||||
|
|||||||
+3
-3
@@ -21,10 +21,10 @@ func newInTerm(tk *Token) (inst *term) {
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func evalIn(ctx ExprContext, self *term) (v any, err error) {
|
func evalIn(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ func evalIn(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
dict, _ := rightValue.(*DictType)
|
dict, _ := rightValue.(*DictType)
|
||||||
v = dict.hasKey(leftValue)
|
v = dict.hasKey(leftValue)
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-10
@@ -16,37 +16,40 @@ func newIncludeTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalInclude(ctx ExprContext, self *term) (v any, err error) {
|
func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if IsList(childValue) {
|
if IsList(childValue) {
|
||||||
list, _ := childValue.([]any)
|
list, _ := childValue.(*ListType)
|
||||||
for i, filePathSpec := range list {
|
for i, filePathSpec := range *list {
|
||||||
if filePath, ok := filePathSpec.(string); ok {
|
if filePath, ok := filePathSpec.(string); ok {
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
count++
|
count++
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("can't load file %q", filePath)
|
err = opTerm.Errorf("can't load file %q", filePath)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
|
err = opTerm.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if IsString(childValue) {
|
} else if IsString(childValue) {
|
||||||
filePath, _ := childValue.(string)
|
filePath, _ := childValue.(string)
|
||||||
v, err = EvalFile(ctx, filePath)
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err != nil {
|
||||||
v = count
|
//v = count
|
||||||
|
v = nil
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-23
@@ -15,7 +15,7 @@ func newIndexTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
|
func verifyKey(indexList *ListType) (index any, err error) {
|
||||||
index = (*indexList)[0]
|
index = (*indexList)[0]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -59,18 +59,18 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
var indexList *ListType
|
var indexList *ListType
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := self.children[1]
|
indexTerm := opTerm.children[1]
|
||||||
if indexList, ok = rightValue.(*ListType); !ok {
|
if indexList, ok = rightValue.(*ListType); !ok {
|
||||||
err = self.Errorf("invalid index expression")
|
err = opTerm.Errorf("invalid index expression")
|
||||||
return
|
return
|
||||||
} else if len(*indexList) != 1 {
|
} else if len(*indexList) != 1 {
|
||||||
err = indexTerm.Errorf("one index only is allowed")
|
err = indexTerm.Errorf("one index only is allowed")
|
||||||
@@ -90,16 +90,9 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = string(unboxedValue[index])
|
v = string(unboxedValue[index])
|
||||||
}
|
}
|
||||||
case *DictType:
|
case *DictType:
|
||||||
/* var ok bool
|
|
||||||
var indexValue any
|
|
||||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
|
||||||
if v, ok = (*unboxedValue)[indexValue]; !ok {
|
|
||||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
||||||
default:
|
default:
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
} else if isIntPair((*indexList)[0]) {
|
} else if isIntPair((*indexList)[0]) {
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
@@ -115,18 +108,10 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = unboxedValue[start:end]
|
v = unboxedValue[start:end]
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
} else if IsDict(leftValue) {
|
} else if IsDict(leftValue) {
|
||||||
d := leftValue.(*DictType)
|
d := leftValue.(*DictType)
|
||||||
|
|
||||||
/* var ok bool
|
|
||||||
var indexValue any
|
|
||||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
|
||||||
if v, ok = (*d)[indexValue]; !ok {
|
|
||||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -136,7 +121,7 @@ func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue a
|
|||||||
var ok bool
|
var ok bool
|
||||||
var indexValue any
|
var indexValue any
|
||||||
|
|
||||||
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
|
if indexValue, err = verifyKey(indexList); err == nil {
|
||||||
if v, ok = (*d)[indexValue]; !ok {
|
if v, ok = (*d)[indexValue]; !ok {
|
||||||
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -26,10 +26,10 @@ func newAppendTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalInsert(ctx ExprContext, self *term) (v any, err error) {
|
func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,19 +37,19 @@ func evalInsert(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
list, _ := rightValue.(*ListType)
|
list, _ := rightValue.(*ListType)
|
||||||
newList := append(ListType{leftValue}, *list...)
|
newList := append(ListType{leftValue}, *list...)
|
||||||
v = &newList
|
v = &newList
|
||||||
if self.children[1].symbol() == SymVariable {
|
if opTerm.children[1].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(self.children[1].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAppend(ctx ExprContext, self *term) (v any, err error) {
|
func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,11 +57,11 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
list, _ := leftValue.(*ListType)
|
list, _ := leftValue.(*ListType)
|
||||||
newList := append(*list, rightValue)
|
newList := append(*list, rightValue)
|
||||||
v = &newList
|
v = &newList
|
||||||
if self.children[0].symbol() == SymVariable {
|
if opTerm.children[0].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(self.children[0].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,22 +16,23 @@ func newIterValueTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalIterValue(ctx ExprContext, self *term) (v any, err error) {
|
func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if it, ok := childValue.(Iterator); ok {
|
if it, ok := childValue.(Iterator); ok {
|
||||||
v, err = it.Current()
|
v, err = it.Current()
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||||
|
registerTermConstructor(SymCaret, newIterValueTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-10
@@ -16,10 +16,10 @@ func newLengthTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,18 +30,18 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
s, _ := childValue.(string)
|
s, _ := childValue.(string)
|
||||||
v = int64(len(s))
|
v = int64(len(s))
|
||||||
} else if IsDict(childValue) {
|
} else if IsDict(childValue) {
|
||||||
// m, _ := childValue.(map[any]any)
|
|
||||||
m, _ := childValue.(*DictType)
|
m, _ := childValue.(*DictType)
|
||||||
v = int64(len(*m))
|
v = int64(len(*m))
|
||||||
} else if it, ok := childValue.(Iterator); ok {
|
} else if it, ok := childValue.(Iterator); ok {
|
||||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
v = int64(it.Count())
|
||||||
count, _ := extIt.CallOperation(countName, nil)
|
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
||||||
v, _ = ToGoInt(count, "")
|
// count, _ := extIt.CallOperation(CountName, nil)
|
||||||
} else {
|
// v, _ = ToGoInt(count, "")
|
||||||
v = int64(it.Index() + 1)
|
// } else {
|
||||||
}
|
// v = int64(it.Index() + 1)
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-26
@@ -4,10 +4,6 @@
|
|||||||
// operator-plugin.go
|
// operator-plugin.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- plugin term
|
//-------- plugin term
|
||||||
|
|
||||||
func newPluginTerm(tk *Token) (inst *term) {
|
func newPluginTerm(tk *Token) (inst *term) {
|
||||||
@@ -20,33 +16,15 @@ func newPluginTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
|
func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
var moduleSpec any
|
var count int
|
||||||
|
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
if count, err = importPluginFromSearchPath(childValue); err == nil {
|
||||||
count := 0
|
|
||||||
it := NewAnyIterator(childValue)
|
|
||||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
|
||||||
if module, ok := moduleSpec.(string); ok {
|
|
||||||
if err = importPlugin(dirList, module); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
count++
|
|
||||||
} else {
|
|
||||||
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
v = int64(count)
|
v = int64(count)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
+37
-5
@@ -17,20 +17,51 @@ func newPostIncTerm(tk *Token) *term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
|
func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
if childValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if it, ok := childValue.(Iterator); ok {
|
if it, ok := childValue.(Iterator); ok {
|
||||||
v, err = it.Next()
|
v, err = it.Next()
|
||||||
} else if IsInteger(childValue) && self.children[0].symbol() == SymVariable {
|
} else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||||
v = childValue
|
v = childValue
|
||||||
i, _ := childValue.(int64)
|
i, _ := childValue.(int64)
|
||||||
ctx.SetVar(self.children[0].source(), i+1)
|
ctx.SetVar(opTerm.children[0].source(), i+1)
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(childValue)
|
err = opTerm.errIncompatibleType(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 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 IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||||
|
v = childValue
|
||||||
|
i, _ := childValue.(int64)
|
||||||
|
ctx.SetVar(opTerm.children[0].source(), i-1)
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -38,4 +69,5 @@ func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymDoublePlus, newPostIncTerm)
|
registerTermConstructor(SymDoublePlus, newPostIncTerm)
|
||||||
|
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-20
@@ -14,8 +14,6 @@ import (
|
|||||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
func newMultiplyTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindUnknown,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@@ -23,10 +21,10 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
|
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +43,7 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = leftInt * rightInt
|
v = leftInt * rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -55,8 +53,6 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
func newDivideTerm(tk *Token) (inst *term) {
|
func newDivideTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindUnknown,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@@ -64,10 +60,10 @@ func newDivideTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDivide(ctx ExprContext, self *term) (v any, err error) {
|
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +86,7 @@ func evalDivide(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -107,10 +103,10 @@ func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) {
|
func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,18 +118,16 @@ func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = numAsFloat(leftValue) / d
|
v = numAsFloat(leftValue) / d
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------- reminder term
|
//-------- reminder term
|
||||||
|
|
||||||
func newReminderTerm(tk *Token) (inst *term) {
|
func newRemainderTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classOperator,
|
|
||||||
// kind: kindUnknown,
|
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@@ -141,10 +135,10 @@ func newReminderTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalReminder(ctx ExprContext, self *term) (v any, err error) {
|
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +151,7 @@ func evalReminder(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
v = leftInt % rightInt
|
v = leftInt % rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -167,5 +161,5 @@ func init() {
|
|||||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||||
registerTermConstructor(SymSlash, newDivideTerm)
|
registerTermConstructor(SymSlash, newDivideTerm)
|
||||||
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
|
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
|
||||||
registerTermConstructor(SymPercent, newReminderTerm)
|
registerTermConstructor(SymPercent, newRemainderTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -34,25 +34,25 @@ func newRangeTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalRange(ctx ExprContext, self *term) (v any, err error) {
|
func evalRange(ctx ExprContext, opTerm *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 len(self.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
leftValue = int64(0)
|
leftValue = int64(0)
|
||||||
rightValue = int64(-1)
|
rightValue = int64(-1)
|
||||||
} else if len(self.children) == 1 {
|
} else if len(opTerm.children) == 1 {
|
||||||
if leftValue, err = self.children[0].compute(ctx); err != nil {
|
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rightValue = int64(ConstLastIndex)
|
rightValue = int64(ConstLastIndex)
|
||||||
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
} else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -45,10 +45,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ func newNotEqualTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
if v, err = evalEqual(ctx, self); err == nil {
|
if v, err = evalEqual(ctx, opTerm); err == nil {
|
||||||
b, _ := ToBool(v)
|
b, _ := ToBool(v)
|
||||||
v = !b
|
v = !b
|
||||||
}
|
}
|
||||||
@@ -119,13 +119,13 @@ func lessThan(self *term, a, b any) (isLess bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalLess(ctx ExprContext, self *term) (v any, err error) {
|
func evalLess(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
v, err = lessThan(self, leftValue, rightValue)
|
v, err = lessThan(opTerm, leftValue, rightValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,14 +154,14 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = lessThanOrEqual(self, leftValue, rightValue)
|
v, err = lessThanOrEqual(opTerm, leftValue, rightValue)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -178,14 +178,14 @@ func newGreaterTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalGreater(ctx ExprContext, self *term) (v any, err error) {
|
func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = lessThan(self, rightValue, leftValue)
|
v, err = lessThan(opTerm, rightValue, leftValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,14 +201,14 @@ func newGreaterEqualTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) {
|
func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = lessThanOrEqual(self, rightValue, leftValue)
|
v, err = lessThanOrEqual(opTerm, rightValue, leftValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,19 +39,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalSelector(ctx ExprContext, self *term) (v any, err error) {
|
func evalSelector(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var exprValue any
|
var exprValue any
|
||||||
var match bool
|
var match bool
|
||||||
|
|
||||||
if err = self.checkOperands(); err != nil {
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
exprTerm := self.children[0]
|
exprTerm := opTerm.children[0]
|
||||||
if exprValue, err = exprTerm.compute(ctx); err != nil {
|
if exprValue, err = exprTerm.compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
caseListTerm := self.children[1]
|
caseListTerm := opTerm.children[1]
|
||||||
caseList, _ := caseListTerm.value().([]*term)
|
caseList, _ := caseListTerm.value().([]*term)
|
||||||
for i, caseTerm := range caseList {
|
for i, caseTerm := range caseList {
|
||||||
caseSel := caseTerm.value()
|
caseSel := caseTerm.value()
|
||||||
|
|||||||
+5
-5
@@ -28,29 +28,29 @@ func newMinusSignTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalSign(ctx ExprContext, self *term) (v any, err error) {
|
func evalSign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var rightValue any
|
var rightValue any
|
||||||
|
|
||||||
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if IsFloat(rightValue) {
|
if IsFloat(rightValue) {
|
||||||
if self.tk.Sym == SymChangeSign {
|
if opTerm.tk.Sym == SymChangeSign {
|
||||||
f, _ := rightValue.(float64)
|
f, _ := rightValue.(float64)
|
||||||
v = -f
|
v = -f
|
||||||
} else {
|
} else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
}
|
}
|
||||||
} else if IsInteger(rightValue) {
|
} else if IsInteger(rightValue) {
|
||||||
if self.tk.Sym == SymChangeSign {
|
if opTerm.tk.Sym == SymChangeSign {
|
||||||
i, _ := rightValue.(int64)
|
i, _ := rightValue.(int64)
|
||||||
v = -i
|
v = -i
|
||||||
} else {
|
} else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(rightValue)
|
err = opTerm.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-12
@@ -21,10 +21,10 @@ func newPlusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,12 +44,8 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
rightList, _ = rightValue.(*ListType)
|
rightList, _ = rightValue.(*ListType)
|
||||||
|
|
||||||
sumList := make(ListType, 0, len(*leftList)+len(*rightList))
|
sumList := make(ListType, 0, len(*leftList)+len(*rightList))
|
||||||
for _, item := range *leftList {
|
sumList = append(sumList, *leftList...)
|
||||||
sumList = append(sumList, item)
|
sumList = append(sumList, *rightList...)
|
||||||
}
|
|
||||||
for _, item := range *rightList {
|
|
||||||
sumList = append(sumList, item)
|
|
||||||
}
|
|
||||||
v = &sumList
|
v = &sumList
|
||||||
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
|
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
@@ -64,7 +60,7 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
c.merge(rightDict)
|
c.merge(rightDict)
|
||||||
v = c
|
v = c
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -81,10 +77,10 @@ func newMinusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMinus(ctx ExprContext, self *term) (v any, err error) {
|
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +105,7 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
v = &diffList
|
v = &diffList
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = minusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-unset.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
//-------- unset term
|
||||||
|
|
||||||
|
func newUnsetTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priSign,
|
||||||
|
evalFunc: evalUnset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, err error) {
|
||||||
|
if name, ok := item.(string); ok {
|
||||||
|
var size int
|
||||||
|
if strings.HasSuffix(name, "()") {
|
||||||
|
size = ctx.FuncCount()
|
||||||
|
ctx.DeleteFunc(strings.TrimRight(name, "()"))
|
||||||
|
deleted = ctx.FuncCount() < size
|
||||||
|
} else {
|
||||||
|
size = ctx.VarCount()
|
||||||
|
ctx.DeleteVar(name)
|
||||||
|
deleted = ctx.VarCount() < size
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleType(item)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var childValue any
|
||||||
|
var deleted bool
|
||||||
|
|
||||||
|
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
if IsList(childValue) {
|
||||||
|
list, _ := childValue.(*ListType)
|
||||||
|
for _, item := range *list {
|
||||||
|
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
|
||||||
|
break
|
||||||
|
} else if deleted {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if deleted, err = deleteContextItem(ctx, opTerm, childValue); err == nil && deleted {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = int64(count)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymKwUnset, newUnsetTerm)
|
||||||
|
}
|
||||||
@@ -18,13 +18,19 @@ func NewParser() (p *parser) {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
||||||
|
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() {
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
||||||
args := make([]*term, 0, 10)
|
args := make([]*term, 0, 10)
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
@@ -48,7 +54,7 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||||
// Example: "add = func(x,y) {x+y}
|
// Example: "add = func(x,y) {x+y}
|
||||||
var body *ast
|
var body *ast
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
@@ -57,15 +63,15 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
itemExpected := false
|
itemExpected := false
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
tk = scanner.Next()
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymIdentifier) {
|
if tk.IsSymbol(SymIdentifier) {
|
||||||
param := newTerm(tk)
|
param := newTerm(tk)
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = scanner.Next()
|
tk = parser.Next(scanner)
|
||||||
if tk.Sym == SymEqual {
|
if tk.Sym == SymEqual {
|
||||||
var paramExpr *ast
|
var paramExpr *ast
|
||||||
defaultParamsStarted = true
|
defaultParamsStarted = true
|
||||||
if paramExpr, err = self.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.forceChild(paramExpr.root)
|
param.forceChild(paramExpr.root)
|
||||||
@@ -86,9 +92,9 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
err = tk.ErrorExpectedGot(")")
|
err = tk.ErrorExpectedGot(")")
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = scanner.Next()
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymOpenBrace) {
|
if tk.IsSymbol(SymOpenBrace) {
|
||||||
body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||||
} else {
|
} else {
|
||||||
err = tk.ErrorExpectedGot("{")
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
@@ -104,7 +110,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
||||||
r, c := scanner.lastPos()
|
r, c := scanner.lastPos()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
@@ -112,7 +118,7 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
|
|||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
zeroRequired := scanner.current.Sym == SymColon
|
zeroRequired := scanner.current.Sym == SymColon
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||||
root := subTree.root
|
root := subTree.root
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if !parsingIndeces && root.symbol() == SymColon {
|
if !parsingIndeces && root.symbol() == SymColon {
|
||||||
@@ -153,14 +159,14 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
@@ -184,8 +190,8 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
|
func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
||||||
tk := scanner.Next()
|
tk := parser.Next(scanner)
|
||||||
if tk.Sym == SymError {
|
if tk.Sym == SymError {
|
||||||
err = tk.Error()
|
err = tk.Error()
|
||||||
return
|
return
|
||||||
@@ -194,7 +200,7 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if tk.Sym == SymInteger || tk.Sym == SymString {
|
if tk.Sym == SymInteger || tk.Sym == SymString {
|
||||||
tkSep := scanner.Next()
|
tkSep := parser.Next(scanner)
|
||||||
if tkSep.Sym != SymColon {
|
if tkSep.Sym != SymColon {
|
||||||
err = tkSep.ErrorExpectedGot(":")
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
} else {
|
} else {
|
||||||
@@ -206,14 +212,14 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
args := make(map[any]*term, 0)
|
args := make(map[any]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedBrace && lastSym != SymEos {
|
for lastSym != SymClosedBrace && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
var key any
|
var key any
|
||||||
if key, err = self.parseDictKey(scanner, allowVarRef); err != nil {
|
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||||
break
|
break
|
||||||
} else if key == nil {
|
} else if key == nil {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
@@ -223,10 +229,10 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args[key] = subTree.root
|
args[key] = subTree.root
|
||||||
} else if key != nil {
|
} else /*if key != nil*/ {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("dictionary-value")
|
err = prev.ErrorExpectedGot("dictionary-value")
|
||||||
break
|
break
|
||||||
@@ -248,10 +254,10 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
tk := scanner.Next()
|
tk := parser.Next(scanner)
|
||||||
startRow := tk.row
|
startRow := tk.row
|
||||||
startCol := tk.col
|
startCol := tk.col
|
||||||
if tk.Sym == SymOpenSquare {
|
if tk.Sym == SymOpenSquare {
|
||||||
@@ -259,10 +265,10 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = self.parseList(scanner, false, allowVarRef); err != nil {
|
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = scanner.Next()
|
tk = parser.Next(scanner)
|
||||||
startRow = tk.row
|
startRow = tk.row
|
||||||
startCol = tk.col
|
startCol = tk.col
|
||||||
} else if !defaultCase {
|
} else if !defaultCase {
|
||||||
@@ -270,7 +276,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.Sym == SymOpenBrace {
|
||||||
if caseExpr, err = self.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -296,25 +302,25 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
|||||||
caseTerm.parent = selectorTerm
|
caseTerm.parent = selectorTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
tk := scanner.makeToken(SymSelector, '?')
|
tk := scanner.makeToken(SymSelector, '?')
|
||||||
if selectorTerm, err = tree.addToken2(tk); err != nil {
|
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return self.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return self.parseGeneral(scanner, true, false, termSymbols...)
|
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeACollection(t *term) bool {
|
func couldBeACollection(t *term) bool {
|
||||||
@@ -333,17 +339,18 @@ func couldBeACollection(t *term) bool {
|
|||||||
// return areOut
|
// return areOut
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
var selectorTerm *term = nil
|
var selectorTerm *term = nil
|
||||||
var currentTerm *term = nil
|
var currentTerm *term = nil
|
||||||
var tk *Token
|
var tk *Token
|
||||||
|
|
||||||
tree = NewAst()
|
tree = NewAst()
|
||||||
firstToken := true
|
firstToken := true
|
||||||
// lastSym := SymUnknown
|
// lastSym := SymUnknown
|
||||||
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() {
|
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
||||||
if tk.Sym == SymComment {
|
// if tk.Sym == SymComment {
|
||||||
continue
|
// continue
|
||||||
}
|
// }
|
||||||
|
|
||||||
if tk.Sym == SymSemiColon {
|
if tk.Sym == SymSemiColon {
|
||||||
if allowForest {
|
if allowForest {
|
||||||
@@ -371,21 +378,21 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
||||||
subTree.root.priority = priValue
|
subTree.root.priority = priValue
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
err = tree.addTerm(newExprTerm(subTree.root))
|
||||||
currentTerm = subTree.root
|
currentTerm = subTree.root
|
||||||
}
|
}
|
||||||
case SymFuncCall:
|
case SymFuncCall:
|
||||||
var funcCallTerm *term
|
var funcCallTerm *term
|
||||||
if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
||||||
err = tree.addTerm(funcCallTerm)
|
err = tree.addTerm(funcCallTerm)
|
||||||
currentTerm = funcCallTerm
|
currentTerm = funcCallTerm
|
||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
parsingIndeces := couldBeACollection(currentTerm)
|
parsingIndeces := couldBeACollection(currentTerm)
|
||||||
if listTerm, err = self.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
||||||
if parsingIndeces {
|
if parsingIndeces {
|
||||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||||
indexTerm := newTerm(indexTk)
|
indexTerm := newTerm(indexTk)
|
||||||
@@ -402,24 +409,24 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||||
} else {
|
} else {
|
||||||
var mapTerm *term
|
var mapTerm *term
|
||||||
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
|
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
||||||
err = tree.addTerm(mapTerm)
|
err = tree.addTerm(mapTerm)
|
||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual:
|
||||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
// }
|
// }
|
||||||
case SymFuncDef:
|
case SymFuncDef:
|
||||||
var funcDefTerm *term
|
var funcDefTerm *term
|
||||||
if funcDefTerm, err = self.parseFuncDef(scanner); err == nil {
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||||
err = tree.addTerm(funcDefTerm)
|
err = tree.addTerm(funcDefTerm)
|
||||||
currentTerm = funcDefTerm
|
currentTerm = funcDefTerm
|
||||||
}
|
}
|
||||||
case SymDollarRound:
|
case SymDollarRound:
|
||||||
var iterDefTerm *term
|
var iterDefTerm *term
|
||||||
if iterDefTerm, err = self.parseIterDef(scanner, allowVarRef); err == nil {
|
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
||||||
err = tree.addTerm(iterDefTerm)
|
err = tree.addTerm(iterDefTerm)
|
||||||
currentTerm = iterDefTerm
|
currentTerm = iterDefTerm
|
||||||
}
|
}
|
||||||
@@ -427,16 +434,16 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
if tk.source[0] == '@' && !allowVarRef {
|
if tk.source[0] == '@' && !allowVarRef {
|
||||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
case SymQuestion:
|
case SymQuestion:
|
||||||
if selectorTerm, err = self.parseSelector(scanner, tree, allowVarRef); err == nil {
|
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
||||||
currentTerm = selectorTerm
|
currentTerm = selectorTerm
|
||||||
}
|
}
|
||||||
case SymColon, SymDoubleColon:
|
case SymColon, SymDoubleColon:
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
if selectorTerm != nil {
|
if selectorTerm != nil {
|
||||||
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
currentTerm = caseTerm
|
currentTerm = caseTerm
|
||||||
if tk.Sym == SymDoubleColon {
|
if tk.Sym == SymDoubleColon {
|
||||||
@@ -444,14 +451,16 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
if tk.IsSymbol(SymColon) {
|
if tk.IsSymbol(SymColon) {
|
||||||
// Colon outside a selector term acts like a separator
|
// Colon outside a selector term acts like a separator
|
||||||
firstToken = true
|
firstToken = true
|
||||||
}
|
}
|
||||||
|
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
||||||
|
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||||
@@ -460,6 +469,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
}
|
}
|
||||||
// lastSym = tk.Sym
|
// lastSym = tk.Sym
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = tk.Error()
|
err = tk.Error()
|
||||||
}
|
}
|
||||||
@@ -472,3 +482,49 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
// }
|
// }
|
||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
||||||
|
var opSym Symbol
|
||||||
|
var opString string
|
||||||
|
|
||||||
|
if tree.root != nil {
|
||||||
|
switch tk.Sym {
|
||||||
|
case SymPlusEqual:
|
||||||
|
opString = "+"
|
||||||
|
opSym = SymPlus
|
||||||
|
case SymMinusEqual:
|
||||||
|
opString = "-"
|
||||||
|
opSym = SymMinus
|
||||||
|
case SymStarEqual:
|
||||||
|
opString = "*"
|
||||||
|
opSym = SymStar
|
||||||
|
default:
|
||||||
|
err = tk.Errorf("unsopported operator %q", tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
leftExpr := tree.root.Clone()
|
||||||
|
leftExpr.setParent(nil)
|
||||||
|
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
||||||
|
t = leftExpr
|
||||||
|
if err = tree.addTerm(leftExpr); err == nil {
|
||||||
|
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
||||||
|
|
||||||
|
var subTree *ast
|
||||||
|
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
||||||
|
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
||||||
|
if err = scanner.UnreadToken(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subTree.root.priority = priValue
|
||||||
|
err = tree.addTerm(newExprTerm(subTree.root))
|
||||||
|
t = subTree.root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
+25
-1
@@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"plugin"
|
"plugin"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -29,7 +30,7 @@ func pluginExists(name string) (exists bool) {
|
|||||||
|
|
||||||
func makePluginName(name string) (decorated string) {
|
func makePluginName(name string) (decorated string) {
|
||||||
var template string
|
var template string
|
||||||
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 {
|
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
||||||
template = "expr-%s-plugin.so"
|
template = "expr-%s-plugin.so"
|
||||||
} else {
|
} else {
|
||||||
template = "expr-%s-plugin.so.debug"
|
template = "expr-%s-plugin.so.debug"
|
||||||
@@ -90,6 +91,29 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func importPluginFromSearchPath(name any) (count int, err error) {
|
||||||
|
var moduleSpec any
|
||||||
|
|
||||||
|
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
||||||
|
count = 0
|
||||||
|
it := NewAnyIterator(name)
|
||||||
|
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||||
|
if module, ok := moduleSpec.(string); ok {
|
||||||
|
if err = importPlugin(dirList, module); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func loadModules(dirList []string, moduleNames []string) (err error) {
|
func loadModules(dirList []string, moduleNames []string) (err error) {
|
||||||
for _, name := range moduleNames {
|
for _, name := range moduleNames {
|
||||||
if err1 := importPlugin(dirList, name); err1 != nil {
|
if err1 := importPlugin(dirList, name); err1 != nil {
|
||||||
|
|||||||
+207
-187
@@ -16,6 +16,7 @@ import (
|
|||||||
type scanner struct {
|
type scanner struct {
|
||||||
current *Token
|
current *Token
|
||||||
prev *Token
|
prev *Token
|
||||||
|
stage *Token
|
||||||
stream *bufio.Reader
|
stream *bufio.Reader
|
||||||
row int
|
row int
|
||||||
column int
|
column int
|
||||||
@@ -49,255 +50,274 @@ func DefaultTranslations() map[Symbol]Symbol {
|
|||||||
// return self.current
|
// return self.current
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func (self *scanner) readChar() (ch byte, err error) {
|
func (scanner *scanner) readChar() (ch byte, err error) {
|
||||||
if ch, err = self.stream.ReadByte(); err == nil {
|
if ch, err = scanner.stream.ReadByte(); err == nil {
|
||||||
if ch == '\n' {
|
if ch == '\n' {
|
||||||
self.row++
|
scanner.row++
|
||||||
self.column = 0
|
scanner.column = 0
|
||||||
} else {
|
} else {
|
||||||
self.column++
|
scanner.column++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) unreadChar() (err error) {
|
func (scanner *scanner) unreadChar() (err error) {
|
||||||
if err = self.stream.UnreadByte(); err == nil {
|
if err = scanner.stream.UnreadByte(); err == nil {
|
||||||
if self.column--; self.column == 0 {
|
if scanner.column--; scanner.column == 0 {
|
||||||
if self.row--; self.row == 0 {
|
if scanner.row--; scanner.row == 0 {
|
||||||
err = errors.New("unread beyond the stream boundary")
|
err = errors.New("unread beyond the stream boundary")
|
||||||
} else {
|
} else {
|
||||||
self.column = 1
|
scanner.column = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) lastPos() (r, c int) {
|
func (scanner *scanner) UnreadToken() (err error) {
|
||||||
if self.prev != nil {
|
if scanner.stage == nil {
|
||||||
r = self.prev.row
|
scanner.stage = scanner.current
|
||||||
c = self.prev.col
|
scanner.current = scanner.prev
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("staging already present, currently one level only of staging is allowed")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) Previous() *Token {
|
func (scanner *scanner) lastPos() (r, c int) {
|
||||||
return self.prev
|
if scanner.prev != nil {
|
||||||
|
r = scanner.prev.row
|
||||||
|
c = scanner.prev.col
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) Next() (tk *Token) {
|
func (scanner *scanner) Previous() *Token {
|
||||||
self.prev = self.current
|
return scanner.prev
|
||||||
tk = self.current
|
}
|
||||||
self.current = self.fetchNextToken()
|
|
||||||
|
func (scanner *scanner) Next() (tk *Token) {
|
||||||
|
scanner.prev = scanner.current
|
||||||
|
tk = scanner.current
|
||||||
|
if scanner.stage != nil {
|
||||||
|
scanner.current = scanner.stage
|
||||||
|
scanner.stage = nil
|
||||||
|
} else {
|
||||||
|
scanner.current = scanner.fetchNextToken()
|
||||||
|
}
|
||||||
return tk
|
return tk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchNextToken() (tk *Token) {
|
func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||||
var ch byte
|
var ch byte
|
||||||
if err := self.skipBlanks(); err != nil {
|
if err := scanner.skipBlanks(); err != nil {
|
||||||
return self.makeErrorToken(err)
|
return scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
escape := false
|
escape := false
|
||||||
for {
|
for {
|
||||||
ch, _ = self.readChar()
|
ch, _ = scanner.readChar()
|
||||||
switch ch {
|
switch ch {
|
||||||
case '+':
|
case '+':
|
||||||
if next, _ := self.peek(); next == '+' {
|
if next, _ := scanner.peek(); next == '+' {
|
||||||
tk = self.moveOn(SymDoublePlus, ch, next)
|
tk = scanner.moveOn(SymDoublePlus, ch, next)
|
||||||
} else if next == '=' {
|
} else if next == '=' {
|
||||||
tk = self.moveOn(SymPlusEqual, ch, next)
|
tk = scanner.moveOn(SymPlusEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymPlus, ch)
|
tk = scanner.makeToken(SymPlus, ch)
|
||||||
}
|
}
|
||||||
case '-':
|
case '-':
|
||||||
if next, _ := self.peek(); next == '-' {
|
if next, _ := scanner.peek(); next == '-' {
|
||||||
tk = self.moveOn(SymDoubleMinus, ch, next)
|
tk = scanner.moveOn(SymDoubleMinus, ch, next)
|
||||||
} else if next == '=' {
|
} else if next == '=' {
|
||||||
tk = self.moveOn(SymMinusEqual, ch, next)
|
tk = scanner.moveOn(SymMinusEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymMinus, ch)
|
tk = scanner.makeToken(SymMinus, ch)
|
||||||
}
|
}
|
||||||
case '*':
|
case '*':
|
||||||
if next, _ := self.peek(); next == '*' {
|
if next, _ := scanner.peek(); next == '*' {
|
||||||
tk = self.moveOn(SymDoubleStar, ch, next)
|
tk = scanner.moveOn(SymDoubleStar, ch, next)
|
||||||
// } else if next == '/' {
|
// } else if next == '/' {
|
||||||
// tk = self.moveOn(SymClosedComment, ch, next)
|
// tk = self.moveOn(SymClosedComment, ch, next)
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymStarEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymStar, ch)
|
tk = scanner.makeToken(SymStar, ch)
|
||||||
}
|
}
|
||||||
case '/':
|
case '/':
|
||||||
if next, _ := self.peek(); next == '*' {
|
if next, _ := scanner.peek(); next == '*' {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
tk = self.fetchBlockComment()
|
tk = scanner.fetchBlockComment()
|
||||||
} else if next == '/' {
|
} else if next == '/' {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
tk = self.fetchOnLineComment()
|
tk = scanner.fetchOnLineComment()
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymSlash, ch)
|
tk = scanner.makeToken(SymSlash, ch)
|
||||||
}
|
}
|
||||||
case '\\':
|
case '\\':
|
||||||
if escape {
|
if escape {
|
||||||
tk = self.makeToken(SymBackSlash, ch)
|
tk = scanner.makeToken(SymBackSlash, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
escape = true
|
escape = true
|
||||||
}
|
}
|
||||||
case '|':
|
case '|':
|
||||||
if next, _ := self.peek(); next == '|' {
|
if next, _ := scanner.peek(); next == '|' {
|
||||||
tk = self.moveOn(SymDoubleVertBar, ch, next)
|
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymVertBar, ch)
|
tk = scanner.makeToken(SymVertBar, ch)
|
||||||
}
|
}
|
||||||
case ',':
|
case ',':
|
||||||
tk = self.makeToken(SymComma, ch)
|
tk = scanner.makeToken(SymComma, ch)
|
||||||
case '^':
|
case '^':
|
||||||
tk = self.makeToken(SymCaret, ch)
|
tk = scanner.makeToken(SymCaret, ch)
|
||||||
case ':':
|
case ':':
|
||||||
if next, _ := self.peek(); next == ':' {
|
if next, _ := scanner.peek(); next == ':' {
|
||||||
tk = self.moveOn(SymDoubleColon, ch, next)
|
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymColon, ch)
|
tk = scanner.makeToken(SymColon, ch)
|
||||||
}
|
}
|
||||||
case ';':
|
case ';':
|
||||||
tk = self.makeToken(SymSemiColon, ch)
|
tk = scanner.makeToken(SymSemiColon, ch)
|
||||||
case '.':
|
case '.':
|
||||||
//if next, _ := self.peek(); next >= '0' && next <= '9' {
|
//if next, _ := self.peek(); next >= '0' && next <= '9' {
|
||||||
// tk = self.parseNumber(ch)
|
// tk = self.parseNumber(ch)
|
||||||
//} else if next == '/' {
|
//} else if next == '/' {
|
||||||
if next, _ := self.peek(); next == '/' {
|
if next, _ := scanner.peek(); next == '/' {
|
||||||
tk = self.moveOn(SymDotSlash, ch, next)
|
tk = scanner.moveOn(SymDotSlash, ch, next)
|
||||||
} else if next == '.' {
|
} else if next == '.' {
|
||||||
if next1, _ := self.peek(); next1 == '.' {
|
if next1, _ := scanner.peek(); next1 == '.' {
|
||||||
tk = self.moveOn(SymTripleDot, ch, next, next1)
|
tk = scanner.moveOn(SymTripleDot, ch, next, next1)
|
||||||
} else {
|
} else {
|
||||||
tk = self.moveOn(SymDoubleDot, ch, next)
|
tk = scanner.moveOn(SymDoubleDot, ch, next)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymDot, ch)
|
tk = scanner.makeToken(SymDot, ch)
|
||||||
}
|
}
|
||||||
case '\'':
|
case '\'':
|
||||||
if escape {
|
if escape {
|
||||||
tk = self.makeToken(SymQuote, ch)
|
tk = scanner.makeToken(SymQuote, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
tk = self.fetchString(ch)
|
tk = scanner.fetchString(ch)
|
||||||
}
|
}
|
||||||
case '"':
|
case '"':
|
||||||
if escape {
|
if escape {
|
||||||
tk = self.makeToken(SymDoubleQuote, ch)
|
tk = scanner.makeToken(SymDoubleQuote, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
tk = self.fetchString(ch)
|
tk = scanner.fetchString(ch)
|
||||||
}
|
}
|
||||||
case '`':
|
case '`':
|
||||||
tk = self.makeToken(SymBackTick, ch)
|
tk = scanner.makeToken(SymBackTick, ch)
|
||||||
case '!':
|
case '!':
|
||||||
if next, _ := self.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = self.moveOn(SymNotEqual, ch, next)
|
tk = scanner.moveOn(SymNotEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymExclamation, ch)
|
tk = scanner.makeToken(SymExclamation, ch)
|
||||||
}
|
}
|
||||||
case '?':
|
case '?':
|
||||||
if next, _ := self.peek(); next == '?' {
|
if next, _ := scanner.peek(); next == '?' {
|
||||||
tk = self.moveOn(SymDoubleQuestion, ch, next)
|
tk = scanner.moveOn(SymDoubleQuestion, ch, next)
|
||||||
} else if next, _ := self.peek(); next == '=' {
|
} else if next == '=' {
|
||||||
tk = self.moveOn(SymQuestionEqual, ch, next)
|
tk = scanner.moveOn(SymQuestionEqual, ch, next)
|
||||||
|
} else if next == '!' {
|
||||||
|
tk = scanner.moveOn(SymQuestionExclam, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymQuestion, ch)
|
tk = scanner.makeToken(SymQuestion, ch)
|
||||||
}
|
}
|
||||||
case '&':
|
case '&':
|
||||||
if next, _ := self.peek(); next == '&' {
|
if next, _ := scanner.peek(); next == '&' {
|
||||||
tk = self.moveOn(SymDoubleAmpersand, ch, next)
|
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymAmpersand, ch)
|
tk = scanner.makeToken(SymAmpersand, ch)
|
||||||
}
|
}
|
||||||
case '%':
|
case '%':
|
||||||
tk = self.makeToken(SymPercent, ch)
|
tk = scanner.makeToken(SymPercent, ch)
|
||||||
case '#':
|
case '#':
|
||||||
tk = self.makeToken(SymHash, ch)
|
tk = scanner.makeToken(SymHash, ch)
|
||||||
case '@':
|
case '@':
|
||||||
if next, _ := self.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
|
if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
if tk = self.fetchIdentifier(next); tk.Sym == SymIdentifier {
|
if tk = scanner.fetchIdentifier(next); tk.Sym == SymIdentifier {
|
||||||
//tk.Sym = SymIdRef
|
//tk.Sym = SymIdRef
|
||||||
tk.source = "@" + tk.source
|
tk.source = "@" + tk.source
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source))
|
tk = scanner.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source))
|
||||||
}
|
}
|
||||||
} else if next == '@' {
|
} else if next == '@' {
|
||||||
tk = self.moveOn(SymDoubleAt, ch, next)
|
tk = scanner.moveOn(SymDoubleAt, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymAt, ch)
|
tk = scanner.makeToken(SymAt, ch)
|
||||||
}
|
}
|
||||||
case '_':
|
case '_':
|
||||||
tk = self.makeToken(SymUndescore, ch)
|
tk = scanner.makeToken(SymUndescore, ch)
|
||||||
case '=':
|
case '=':
|
||||||
if next, _ := self.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = self.moveOn(SymDoubleEqual, ch, next)
|
tk = scanner.moveOn(SymDoubleEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymEqual, ch)
|
tk = scanner.makeToken(SymEqual, ch)
|
||||||
}
|
}
|
||||||
case '<':
|
case '<':
|
||||||
if next, _ := self.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = self.moveOn(SymLessOrEqual, ch, next)
|
tk = scanner.moveOn(SymLessOrEqual, ch, next)
|
||||||
} else if next == '<' {
|
} else if next == '<' {
|
||||||
tk = self.moveOn(SymAppend, ch, next)
|
tk = scanner.moveOn(SymAppend, ch, next)
|
||||||
} else if next == '>' {
|
} else if next == '>' {
|
||||||
tk = self.moveOn(SymLessGreater, ch, next)
|
tk = scanner.moveOn(SymLessGreater, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymLess, ch)
|
tk = scanner.makeToken(SymLess, ch)
|
||||||
}
|
}
|
||||||
case '>':
|
case '>':
|
||||||
if next, _ := self.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = self.moveOn(SymGreaterOrEqual, ch, next)
|
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
|
||||||
} else if next == '>' {
|
} else if next == '>' {
|
||||||
tk = self.moveOn(SymInsert, ch, next)
|
tk = scanner.moveOn(SymInsert, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymGreater, ch)
|
tk = scanner.makeToken(SymGreater, ch)
|
||||||
}
|
}
|
||||||
case '$':
|
case '$':
|
||||||
if next, _ := self.peek(); next == '(' {
|
if next, _ := scanner.peek(); next == '(' {
|
||||||
tk = self.moveOn(SymDollarRound, ch, next)
|
tk = scanner.moveOn(SymDollarRound, ch, next)
|
||||||
tk.source += ")"
|
tk.source += ")"
|
||||||
} else if next == '$' {
|
} else if next == '$' {
|
||||||
tk = self.moveOn(SymDoubleDollar, ch, next)
|
tk = scanner.moveOn(SymDoubleDollar, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymDollar, ch)
|
tk = scanner.makeToken(SymDollar, ch)
|
||||||
}
|
}
|
||||||
case '(':
|
case '(':
|
||||||
if next, _ := self.peek(); next == ')' {
|
// if next, _ := scanner.peek(); next == ')' {
|
||||||
tk = self.moveOn(SymOpenClosedRound, ch, next)
|
// tk = scanner.moveOn(SymOpenClosedRound, ch, next)
|
||||||
} else {
|
// } else {
|
||||||
tk = self.makeToken(SymOpenRound, ch)
|
tk = scanner.makeToken(SymOpenRound, ch)
|
||||||
}
|
// }
|
||||||
case ')':
|
case ')':
|
||||||
tk = self.makeToken(SymClosedRound, ch)
|
tk = scanner.makeToken(SymClosedRound, ch)
|
||||||
case '[':
|
case '[':
|
||||||
tk = self.makeToken(SymOpenSquare, ch)
|
tk = scanner.makeToken(SymOpenSquare, ch)
|
||||||
case ']':
|
case ']':
|
||||||
tk = self.makeToken(SymClosedSquare, ch)
|
tk = scanner.makeToken(SymClosedSquare, ch)
|
||||||
case '{':
|
case '{':
|
||||||
tk = self.makeToken(SymOpenBrace, ch)
|
tk = scanner.makeToken(SymOpenBrace, ch)
|
||||||
case '}':
|
case '}':
|
||||||
tk = self.makeToken(SymClosedBrace, ch)
|
tk = scanner.makeToken(SymClosedBrace, ch)
|
||||||
case '~':
|
case '~':
|
||||||
tk = self.makeToken(SymTilde, ch)
|
tk = scanner.makeToken(SymTilde, ch)
|
||||||
case 0:
|
case 0:
|
||||||
if escape {
|
if escape {
|
||||||
tk = self.makeErrorToken(errors.New("incomplete escape sequence"))
|
tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
|
||||||
}
|
}
|
||||||
escape = false
|
escape = false
|
||||||
default:
|
default:
|
||||||
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
|
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
|
||||||
if tk = self.fetchIdentifier(ch); tk.Sym == SymKwFunc {
|
if tk = scanner.fetchIdentifier(ch); tk.Sym == SymKwFunc {
|
||||||
if next, _ := self.peek(); next == '(' {
|
if next, _ := scanner.peek(); next == '(' {
|
||||||
tk = self.moveOn(SymFuncDef, ch, next)
|
tk = scanner.moveOn(SymFuncDef, ch, next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ch >= '0' && ch <= '9' {
|
} else if ch >= '0' && ch <= '9' {
|
||||||
tk = self.parseNumber(ch)
|
tk = scanner.parseNumber(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !escape {
|
if !escape {
|
||||||
@@ -305,14 +325,14 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if tk == nil {
|
if tk == nil {
|
||||||
tk = NewErrorToken(self.row, self.column, fmt.Errorf("unknown symbol '%c'", ch))
|
tk = NewErrorToken(scanner.row, scanner.column, fmt.Errorf("unknown symbol '%c'", ch))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) sync(err error) error {
|
func (scanner *scanner) sync(err error) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = self.unreadChar()
|
err = scanner.unreadChar()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -333,32 +353,32 @@ func isHexDigit(ch byte) bool {
|
|||||||
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
|
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) {
|
func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) {
|
||||||
var ch byte
|
var ch byte
|
||||||
var digitType string
|
var digitType string
|
||||||
firstCh = currentFirstCh
|
firstCh = currentFirstCh
|
||||||
digitFunc = isDecimalDigit
|
digitFunc = isDecimalDigit
|
||||||
numBase = 10
|
numBase = 10
|
||||||
|
|
||||||
if ch, err = self.peek(); err == nil {
|
if ch, err = scanner.peek(); err == nil {
|
||||||
if ch == 'b' || ch == 'B' {
|
if ch == 'b' || ch == 'B' {
|
||||||
numBase = 2
|
numBase = 2
|
||||||
digitType = "binary"
|
digitType = "binary"
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
digitFunc = isBinaryDigit
|
digitFunc = isBinaryDigit
|
||||||
firstCh, err = self.readChar()
|
firstCh, err = scanner.readChar()
|
||||||
} else if ch == 'o' || ch == 'O' {
|
} else if ch == 'o' || ch == 'O' {
|
||||||
numBase = 8
|
numBase = 8
|
||||||
digitType = "octal"
|
digitType = "octal"
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
digitFunc = isOctalDigit
|
digitFunc = isOctalDigit
|
||||||
firstCh, err = self.readChar()
|
firstCh, err = scanner.readChar()
|
||||||
} else if ch == 'x' || ch == 'X' {
|
} else if ch == 'x' || ch == 'X' {
|
||||||
numBase = 16
|
numBase = 16
|
||||||
digitType = "hex"
|
digitType = "hex"
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
digitFunc = isHexDigit
|
digitFunc = isHexDigit
|
||||||
firstCh, err = self.readChar()
|
firstCh, err = scanner.readChar()
|
||||||
}
|
}
|
||||||
if err == nil && !digitFunc(firstCh) {
|
if err == nil && !digitFunc(firstCh) {
|
||||||
if len(digitType) == 0 {
|
if len(digitType) == 0 {
|
||||||
@@ -372,7 +392,7 @@ func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var ch byte
|
var ch byte
|
||||||
var sym Symbol = SymInteger
|
var sym Symbol = SymInteger
|
||||||
@@ -381,9 +401,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
var numBase = 10
|
var numBase = 10
|
||||||
|
|
||||||
if firstCh == '0' {
|
if firstCh == '0' {
|
||||||
firstCh, numBase, isDigit, err = self.initBase(&sb, firstCh)
|
firstCh, numBase, isDigit, err = scanner.initBase(firstCh)
|
||||||
}
|
}
|
||||||
for ch = firstCh; err == nil && isDigit(ch); ch, err = self.readChar() {
|
for ch = firstCh; err == nil && isDigit(ch); ch, err = scanner.readChar() {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,9 +411,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
if err == nil && ch == '.' {
|
if err == nil && ch == '.' {
|
||||||
sym = SymFloat
|
sym = SymFloat
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
ch, err = self.readChar()
|
ch, err = scanner.readChar()
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,32 +422,32 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
if ch == 'e' || ch == 'E' {
|
if ch == 'e' || ch == 'E' {
|
||||||
sym = SymFloat
|
sym = SymFloat
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
if ch, err = self.readChar(); err == nil {
|
if ch, err = scanner.readChar(); err == nil {
|
||||||
if ch == '+' || ch == '-' {
|
if ch == '+' || ch == '-' {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
ch, err = self.readChar()
|
ch, err = scanner.readChar()
|
||||||
}
|
}
|
||||||
if ch >= '0' && ch <= '9' {
|
if ch >= '0' && ch <= '9' {
|
||||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", self.row, self.column, ch)
|
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", scanner.row, scanner.column, ch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ch == '(' {
|
} else if ch == '(' {
|
||||||
sym = SymFraction
|
sym = SymFraction
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
ch, err = self.readChar()
|
ch, err = scanner.readChar()
|
||||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if ch != ')' {
|
if ch != ')' {
|
||||||
err = fmt.Errorf("[%d:%d] expected ')', got '%c'", self.row, self.column, ch)
|
err = fmt.Errorf("[%d:%d] expected ')', got '%c'", scanner.row, scanner.column, ch)
|
||||||
} else {
|
} else {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
_, err = self.readChar()
|
_, err = scanner.readChar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,10 +455,10 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
} else {
|
} else {
|
||||||
var value any
|
var value any
|
||||||
err = self.sync(err)
|
err = scanner.sync(err) // TODO: Check this function
|
||||||
txt := sb.String()
|
txt := sb.String()
|
||||||
if sym == SymFloat {
|
if sym == SymFloat {
|
||||||
value, err = strconv.ParseFloat(txt, 64)
|
value, err = strconv.ParseFloat(txt, 64)
|
||||||
@@ -448,39 +468,39 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
value, err = strconv.ParseInt(txt, numBase, 64)
|
value, err = strconv.ParseInt(txt, numBase, 64)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = self.makeValueToken(sym, txt, value)
|
tk = scanner.makeValueToken(sym, txt, value)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = self.readChar() {
|
for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = scanner.readChar() {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
} else if err = self.sync(err); err != nil && err != io.EOF {
|
} else if err = scanner.sync(err); err != nil && err != io.EOF {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
} else {
|
} else {
|
||||||
txt := sb.String()
|
txt := sb.String()
|
||||||
uptxt := strings.ToUpper(txt)
|
uptxt := strings.ToUpper(txt)
|
||||||
if sym, ok := keywords[uptxt]; ok {
|
if sym, ok := keywords[uptxt]; ok {
|
||||||
tk = self.makeKeywordToken(sym, uptxt)
|
tk = scanner.makeKeywordToken(sym, uptxt)
|
||||||
} else if uptxt == `TRUE` {
|
} else if uptxt == `TRUE` {
|
||||||
tk = self.makeValueToken(SymBool, txt, true)
|
tk = scanner.makeValueToken(SymBool, txt, true)
|
||||||
} else if uptxt == `FALSE` {
|
} else if uptxt == `FALSE` {
|
||||||
tk = self.makeValueToken(SymBool, txt, false)
|
tk = scanner.makeValueToken(SymBool, txt, false)
|
||||||
} else if ch, _ := self.peek(); ch == '(' {
|
} else if ch, _ := scanner.peek(); ch == '(' {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
tk = self.makeValueToken(SymFuncCall, txt+"(", txt)
|
tk = scanner.makeValueToken(SymFuncCall, txt+"(", txt)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeValueToken(SymIdentifier, txt, txt)
|
tk = scanner.makeValueToken(SymIdentifier, txt, txt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,29 +520,29 @@ func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchBlockComment() *Token {
|
func (scanner *scanner) fetchBlockComment() *Token {
|
||||||
return self.fetchUntil(SymComment, false, '*', '/')
|
return scanner.fetchUntil(SymComment, false, '*', '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchOnLineComment() *Token {
|
func (scanner *scanner) fetchOnLineComment() *Token {
|
||||||
return self.fetchUntil(SymComment, true, '\n')
|
return scanner.fetchUntil(SymComment, true, '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) {
|
func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var ch byte
|
var ch byte
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
var value string
|
var value string
|
||||||
ring := NewByteSlider(len(endings))
|
ring := NewByteSlider(len(endings))
|
||||||
endReached := false
|
endReached := false
|
||||||
for ch, err = self.readChar(); err == nil && !endReached; {
|
for ch, err = scanner.readChar(); err == nil && !endReached; {
|
||||||
sb.WriteByte(ch)
|
sb.WriteByte(ch)
|
||||||
ring.PushEnd(ch)
|
ring.PushEnd(ch)
|
||||||
if ring.Equal(endings) {
|
if ring.Equal(endings) {
|
||||||
value = sb.String()[0 : sb.Len()-len(endings)]
|
value = sb.String()[0 : sb.Len()-len(endings)]
|
||||||
endReached = true
|
endReached = true
|
||||||
} else {
|
} else {
|
||||||
ch, err = self.readChar()
|
ch, err = scanner.readChar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !endReached && allowEos {
|
if !endReached && allowEos {
|
||||||
@@ -531,18 +551,18 @@ func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk
|
|||||||
}
|
}
|
||||||
|
|
||||||
if endReached {
|
if endReached {
|
||||||
tk = self.makeValueToken(sym, "", value)
|
tk = scanner.makeValueToken(sym, "", value)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchString(termCh byte) (tk *Token) {
|
func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var ch, prev byte
|
var ch, prev byte
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for ch, err = self.readChar(); err == nil; ch, err = self.readChar() {
|
for ch, err = scanner.readChar(); err == nil; ch, err = scanner.readChar() {
|
||||||
if prev == '\\' {
|
if prev == '\\' {
|
||||||
switch ch {
|
switch ch {
|
||||||
case '"':
|
case '"':
|
||||||
@@ -570,65 +590,65 @@ func (self *scanner) fetchString(termCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
tk = self.makeErrorToken(errors.New("missing string termination \""))
|
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
txt := sb.String()
|
txt := sb.String()
|
||||||
tk = self.makeValueToken(SymString, `"`+txt+`"`, txt)
|
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) peek() (next byte, err error) {
|
func (scanner *scanner) peek() (next byte, err error) {
|
||||||
var one []byte
|
var one []byte
|
||||||
if one, err = self.stream.Peek(1); err == nil {
|
if one, err = scanner.stream.Peek(1); err == nil {
|
||||||
next = one[0]
|
next = one[0]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) skipBlanks() (err error) {
|
func (scanner *scanner) skipBlanks() (err error) {
|
||||||
var one []byte
|
var one []byte
|
||||||
for one, err = self.stream.Peek(1); err == nil && one[0] <= 32; one, err = self.stream.Peek(1) {
|
for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) translate(sym Symbol) Symbol {
|
func (scanner *scanner) translate(sym Symbol) Symbol {
|
||||||
if self.translations != nil {
|
if scanner.translations != nil {
|
||||||
if translatedSym, ok := self.translations[sym]; ok {
|
if translatedSym, ok := scanner.translations[sym]; ok {
|
||||||
return translatedSym
|
return translatedSym
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sym
|
return sym
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||||
tk = NewToken(self.row, self.column, self.translate(sym), string(chars))
|
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||||
for i := 1; i < len(chars); i++ {
|
for i := 1; i < len(chars); i++ {
|
||||||
self.readChar()
|
scanner.readChar()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
|
func (scanner *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
|
||||||
tk = NewToken(self.row, self.column, self.translate(sym), string(chars))
|
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
func (scanner *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
||||||
tk = NewToken(self.row, self.column, self.translate(sym), upperCaseKeyword)
|
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
func (scanner *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
||||||
tk = NewValueToken(self.row, self.column, self.translate(sym), source, value)
|
tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeErrorToken(err error) *Token {
|
func (scanner *scanner) makeErrorToken(err error) *Token {
|
||||||
return NewErrorToken(self.row, self.column, err)
|
return NewErrorToken(scanner.row, scanner.column, err)
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-4
@@ -25,7 +25,7 @@ func NewSimpleStore() *SimpleStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func filterRefName(name string) bool { return name[0] != '@' }
|
func filterRefName(name string) bool { return name[0] != '@' }
|
||||||
func filterPrivName(name string) bool { return name[0] != '_' }
|
//func filterPrivName(name string) bool { return name[0] != '_' }
|
||||||
|
|
||||||
func (ctx *SimpleStore) SetParent(parentCtx ExprContext) {
|
func (ctx *SimpleStore) SetParent(parentCtx ExprContext) {
|
||||||
ctx.parent = parentCtx
|
ctx.parent = parentCtx
|
||||||
@@ -132,6 +132,14 @@ func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varN
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) VarCount() int {
|
||||||
|
return len(ctx.varStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) DeleteVar(varName string) {
|
||||||
|
delete(ctx.varStore, varName)
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
||||||
info, exists = ctx.funcStore[name]
|
info, exists = ctx.funcStore[name]
|
||||||
return
|
return
|
||||||
@@ -141,10 +149,11 @@ func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
|
|||||||
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
|
ctx.funcStore[info.Name()], _ = info.(*funcInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
|
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (exprFunc ExprFunc, err error) {
|
||||||
var info *funcInfo
|
var info *funcInfo
|
||||||
if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
|
if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
|
||||||
ctx.funcStore[name] = info
|
ctx.funcStore[name] = info
|
||||||
|
exprFunc = info
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -163,10 +172,18 @@ func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (fun
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
|
func (ctx *SimpleStore) FuncCount() int {
|
||||||
|
return len(ctx.funcStore)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) DeleteFunc(funcName string) {
|
||||||
|
delete(ctx.funcStore, funcName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) Call(name string, args map[string]any) (result any, err error) {
|
||||||
if info, exists := GetLocalFuncInfo(ctx, name); exists {
|
if info, exists := GetLocalFuncInfo(ctx, name); exists {
|
||||||
functor := info.Functor()
|
functor := info.Functor()
|
||||||
result, err = functor.Invoke(ctx, name, args)
|
result, err = functor.InvokeNamed(ctx, name, args)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,16 +57,18 @@ const (
|
|||||||
SymTilde // 46: '~'
|
SymTilde // 46: '~'
|
||||||
SymDoubleQuestion // 47: '??'
|
SymDoubleQuestion // 47: '??'
|
||||||
SymQuestionEqual // 48: '?='
|
SymQuestionEqual // 48: '?='
|
||||||
SymDoubleAt // 49: '@@'
|
SymQuestionExclam // 49: '?!'
|
||||||
SymDoubleColon // 50: '::'
|
SymDoubleAt // 50: '@@'
|
||||||
SymInsert // 51: '>>'
|
SymDoubleColon // 51: '::'
|
||||||
SymAppend // 52: '<<'
|
SymInsert // 52: '>>'
|
||||||
SymCaret // 53: '^'
|
SymAppend // 53: '<<'
|
||||||
SymDollarRound // 54: '$('
|
SymCaret // 54: '^'
|
||||||
SymOpenClosedRound // 55: '()'
|
SymDollarRound // 55: '$('
|
||||||
SymDoubleDollar // 56: '$$'
|
SymOpenClosedRound // 56: '()'
|
||||||
SymDoubleDot // 57: '..'
|
SymDoubleDollar // 57: '$$'
|
||||||
SymTripleDot // 58: '...'
|
SymDoubleDot // 58: '..'
|
||||||
|
SymTripleDot // 59: '...'
|
||||||
|
SymStarEqual // 60: '*='
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
@@ -105,6 +107,7 @@ const (
|
|||||||
SymKwIn
|
SymKwIn
|
||||||
SymKwInclude
|
SymKwInclude
|
||||||
SymKwNil
|
SymKwNil
|
||||||
|
SymKwUnset
|
||||||
)
|
)
|
||||||
|
|
||||||
var keywords map[string]Symbol
|
var keywords map[string]Symbol
|
||||||
@@ -122,5 +125,6 @@ func init() {
|
|||||||
"NOT": SymKwNot,
|
"NOT": SymKwNot,
|
||||||
"OR": SymKwOr,
|
"OR": SymKwOr,
|
||||||
"NIL": SymKwNil,
|
"NIL": SymKwNil,
|
||||||
|
"UNSET": SymKwUnset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ func TestAddUnknownTokens(t *testing.T) {
|
|||||||
wantErr := errors.New(`unexpected token "%"`)
|
wantErr := errors.New(`unexpected 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -45,13 +45,13 @@ func TestBoolNoShortcut(t *testing.T) {
|
|||||||
/* 5 */ {`not "true"`, false, nil},
|
/* 5 */ {`not "true"`, false, nil},
|
||||||
/* 6 */ {`not "false"`, false, nil},
|
/* 6 */ {`not "false"`, false, nil},
|
||||||
/* 7 */ {`not ""`, true, nil},
|
/* 7 */ {`not ""`, true, nil},
|
||||||
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`)},
|
/* 8 */ {`not []`, nil, `[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`},
|
||||||
/* 9 */ {`true and false`, false, nil},
|
/* 9 */ {`true and false`, false, nil},
|
||||||
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)},
|
/* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`},
|
||||||
/* 11 */ {`[] and false`, nil, errors.New(`[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`)},
|
/* 11 */ {`[] and false`, nil, `[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`},
|
||||||
/* 12 */ {`true or false`, true, nil},
|
/* 12 */ {`true or false`, true, nil},
|
||||||
/* 13 */ {`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
|
/* 13 */ {`true or []`, nil, `[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`},
|
||||||
/* 14 */ {`[] or false`, nil, errors.New(`[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`)},
|
/* 14 */ {`[] or false`, nil, `[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|||||||
+10
-11
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -21,9 +20,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 6 */ {`int(3.1)`, int64(3), nil},
|
/* 6 */ {`int(3.1)`, int64(3), nil},
|
||||||
/* 7 */ {`int(3.9)`, int64(3), nil},
|
/* 7 */ {`int(3.9)`, int64(3), nil},
|
||||||
/* 8 */ {`int("432")`, int64(432), nil},
|
/* 8 */ {`int("432")`, int64(432), nil},
|
||||||
/* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)},
|
/* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
|
||||||
/* 10 */ {`int("432", 4)`, nil, errors.New(`int(): too much params -- expected 1, got 2`)},
|
/* 10 */ {`int("432", 4)`, nil, `int(): too many params -- expected 1, got 2`},
|
||||||
/* 11 */ {`int(nil)`, nil, errors.New(`int(): can't convert nil to int`)},
|
/* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
|
||||||
/* 12 */ {`isInt(2+1)`, true, nil},
|
/* 12 */ {`isInt(2+1)`, true, nil},
|
||||||
/* 13 */ {`isInt(3.1)`, false, nil},
|
/* 13 */ {`isInt(3.1)`, false, nil},
|
||||||
/* 14 */ {`isFloat(3.1)`, true, nil},
|
/* 14 */ {`isFloat(3.1)`, true, nil},
|
||||||
@@ -42,9 +41,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 30 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
|
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
||||||
/* 31 */ {`dec()`, nil, errors.New(`dec(): too few params -- expected 1, got 0`)},
|
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||||
/* 32 */ {`dec(1,2,3)`, nil, errors.New(`dec(): too much params -- expected 1, got 3`)},
|
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||||
/* 33 */ {`isBool(false)`, true, nil},
|
/* 33 */ {`isBool(false)`, true, nil},
|
||||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
||||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||||
@@ -53,15 +52,15 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 38 */ {`bool(1.0)`, true, nil},
|
/* 38 */ {`bool(1.0)`, true, nil},
|
||||||
/* 39 */ {`bool("1")`, true, nil},
|
/* 39 */ {`bool("1")`, true, nil},
|
||||||
/* 40 */ {`bool(false)`, false, nil},
|
/* 40 */ {`bool(false)`, false, nil},
|
||||||
/* 41 */ {`bool([1])`, nil, errors.New(`bool(): can't convert list to bool`)},
|
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
||||||
/* 44 */ {`dec([1])`, nil, errors.New(`dec(): can't convert list to float`)},
|
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||||
// /* 45 */ {`string([1])`, nil, errors.New(`string(): can't convert list to string`)},
|
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 2)
|
//runTestSuiteSpec(t, section, inputs, 10)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func TestFuncFmt(t *testing.T) {
|
|||||||
|
|
||||||
//t.Setenv("EXPR_PATH", ".")
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 19)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ func TestFmt(t *testing.T) {
|
|||||||
|
|
||||||
text := "ciao mondo"
|
text := "ciao mondo"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
|
/* 1 */ {fmt.Sprintf(`builtin "fmt"; println("%s")`, text), int64(11), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
|
|||||||
|
|
||||||
t.Setenv("EXPR_PATH", "test-resources")
|
t.Setenv("EXPR_PATH", "test-resources")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
//runTestSuiteSpec(t, section, inputs, 1)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_builtin-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFuncRun(t *testing.T) {
|
||||||
|
section := "Builtin-Iterator"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
|
||||||
|
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
|
||||||
|
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){status=status+item; true}, {"status":0})`, int64(6), nil},
|
||||||
|
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
|
||||||
|
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
|
||||||
|
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, nil},
|
||||||
|
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
|
||||||
|
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
|
||||||
|
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
|
||||||
|
}
|
||||||
|
|
||||||
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
//runTestSuiteSpec(t, section, inputs, 1)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,16 +13,21 @@ func TestFuncMathArith(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
||||||
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
|
||||||
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)},
|
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, `unknown function mulX()`},
|
||||||
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
|
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
|
||||||
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
|
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
|
||||||
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
|
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
|
||||||
/* 7 */ {`builtin "math.arith"; a=5; b=2; add(a, b*3)`, int64(11), nil},
|
/* 7 */ {`builtin "math.arith"; a=5; b=2; add(a, b*3)`, int64(11), nil},
|
||||||
/* 8 */ {`builtin "math.arith"; var2="abc"; add(1,2) but var2`, "abc", nil},
|
/* 8 */ {`builtin "math.arith"; var2="abc"; add(1,2) but var2`, "abc", nil},
|
||||||
|
/* 9 */ {`builtin "math.arith"; add()`, int64(0), nil},
|
||||||
|
/* 10 */ {`builtin "math.arith"; mul()`, int64(1), nil},
|
||||||
|
/* 11 */ {`builtin "math.arith"; add([1,2,3])`, int64(6), nil},
|
||||||
|
/* 12 */ {`builtin "math.arith"; mul([2,2,3])`, int64(12), nil},
|
||||||
|
/* 13 */ {`builtin "math.arith"; mul(2,2,3)`, int64(12), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
//runTestSuiteSpec(t, section, inputs, 10)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-11
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,23 +13,23 @@ func TestFuncOs(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
/* 1 */ {`builtin "os.file"`, int64(1), nil},
|
||||||
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
|
||||||
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
|
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, `open /etc/hostsX: no such file or directory`},
|
||||||
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
|
||||||
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
|
||||||
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
|
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
|
||||||
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
|
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, `fileReadText(): invalid file handle`},
|
||||||
/* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
|
/* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, `fileWriteText(): invalid file handle`},
|
||||||
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, errors.New(`fileOpen(): too few params -- expected 1, got 0`)},
|
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, `fileOpen(): too few params -- expected 1, got 0`},
|
||||||
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, errors.New(`fileOpen(): missing or invalid file path`)},
|
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, `fileOpen(): missing or invalid file path`},
|
||||||
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, errors.New(`fileCreate(): missing or invalid file path`)},
|
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, `fileCreate(): missing or invalid file path`},
|
||||||
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, errors.New(`fileAppend(): missing or invalid file path`)},
|
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, `fileAppend(): missing or invalid file path`},
|
||||||
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, errors.New(`fileClose(): invalid file handle`)},
|
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
|
||||||
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
||||||
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, errors.New(`fileReadTextAll(): invalid file handle 123 [int64]`)},
|
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 69)
|
//runTestSuiteSpec(t, section, inputs, 2)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,8 +15,8 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
||||||
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
||||||
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
||||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)},
|
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
|
||||||
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)},
|
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
||||||
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
||||||
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||||
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
|
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
|
||||||
@@ -25,13 +24,13 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
|
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
|
||||||
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
|
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
|
||||||
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
|
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)},
|
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, `strStartsWith(): too few params -- expected 2 or more, got 1`},
|
||||||
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
|
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
|
||||||
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
|
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
|
||||||
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)},
|
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`},
|
||||||
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||||
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)},
|
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
||||||
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)},
|
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
||||||
|
|
||||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-19
@@ -5,6 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
type inputType struct {
|
type inputType struct {
|
||||||
source string
|
source string
|
||||||
wantResult any
|
wantResult any
|
||||||
wantErr error
|
wantErr any
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
|
func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
|
||||||
@@ -41,6 +42,8 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
|
|||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
|
// fmt.Printf("%3d: %s\n", i+1, input.source)
|
||||||
|
|
||||||
good := doTest(t, ctx, section, &input, i+1)
|
good := doTest(t, ctx, section, &input, i+1)
|
||||||
if good {
|
if good {
|
||||||
succeeded++
|
succeeded++
|
||||||
@@ -52,20 +55,17 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
|
|||||||
}
|
}
|
||||||
func runTestSuite(t *testing.T, section string, inputs []inputType) {
|
func runTestSuite(t *testing.T, section string, inputs []inputType) {
|
||||||
runCtxTestSuite(t, nil, section, inputs)
|
runCtxTestSuite(t, nil, section, inputs)
|
||||||
/*
|
}
|
||||||
succeeded := 0
|
|
||||||
failed := 0
|
|
||||||
|
|
||||||
for i, input := range inputs {
|
func getWantedError(input *inputType) error {
|
||||||
good := doTest(t, nil, section, &input, i+1)
|
var wantErr error
|
||||||
if good {
|
var ok bool
|
||||||
succeeded++
|
if wantErr, ok = input.wantErr.(error); !ok {
|
||||||
} else {
|
if msg, ok := input.wantErr.(string); ok {
|
||||||
failed++
|
wantErr = errors.New(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
return wantErr
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
|
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
|
||||||
@@ -73,12 +73,14 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
|||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
|
||||||
|
wantErr := getWantedError(input)
|
||||||
|
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = NewSimpleStore()
|
ctx = NewSimpleStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
logTest(t, count, section, input.source, input.wantResult, input.wantErr)
|
logTest(t, count, section, input.source, input.wantResult, wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
@@ -91,13 +93,13 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
|||||||
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
||||||
|
|
||||||
if !eq /*gotResult != input.wantResult*/ {
|
if !eq /*gotResult != input.wantResult*/ {
|
||||||
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
|
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
|
||||||
good = false
|
good = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if gotErr != input.wantErr {
|
if gotErr != wantErr {
|
||||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
|
||||||
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr)
|
t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
|
||||||
good = false
|
good = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,8 +108,8 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
|||||||
|
|
||||||
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
||||||
if wantErr == nil {
|
if wantErr == nil {
|
||||||
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
t.Logf("[+]%s nr %3d -- `%s` --> %v", section, n, source, wantResult)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
|
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-2
@@ -22,7 +22,7 @@ func TestDictParser(t *testing.T) {
|
|||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||||
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
|
||||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||||
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
||||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||||
@@ -32,7 +32,12 @@ func TestDictParser(t *testing.T) {
|
|||||||
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
|
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
|
||||||
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
||||||
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
||||||
}
|
/* 12 */ {`m={
|
||||||
|
"a":1,
|
||||||
|
//"b":2,
|
||||||
|
"c":3
|
||||||
|
}`, map[any]any{"a": 1, "c": 3}, nil},
|
||||||
|
}
|
||||||
|
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|||||||
+15
-4
@@ -17,10 +17,21 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||||
/* 6 */ {`
|
/* 6 */ {`a=3; a+=1`, int64(4), nil},
|
||||||
|
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
||||||
|
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
||||||
|
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||||
|
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||||
|
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
||||||
|
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
||||||
|
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||||
|
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||||
|
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||||
|
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||||
|
/* 17 */ {`
|
||||||
ds={
|
ds={
|
||||||
"init":func(end){@end=end; @current=0 but true},
|
"init":func(@end){@current=0 but true},
|
||||||
"current":func(){current},
|
//"current":func(){current},
|
||||||
"next":func(){
|
"next":func(){
|
||||||
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
|
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
|
||||||
}
|
}
|
||||||
@@ -32,6 +43,6 @@ func TestExpr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 3)
|
// runTestSuiteSpec(t, section, inputs, 17)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,21 +17,22 @@ func TestFractionsParser(t *testing.T) {
|
|||||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
||||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
||||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
||||||
/* 7 */ {`1|"5"`, nil, errors.New(`denominator must be integer, got string (5)`)},
|
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
||||||
/* 8 */ {`"1"|5`, nil, errors.New(`numerator must be integer, got string (1)`)},
|
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
||||||
/* 9 */ {`1|+5`, nil, errors.New(`[1:3] infix operator "|" requires two non-nil operands, got 1`)},
|
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
||||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
||||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
||||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
||||||
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
||||||
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 15 */ {`1|0`, nil, errors.New(`division by zero`)},
|
/* 15 */ {`1|0`, nil, `division by zero`},
|
||||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||||
/* 17 */ {`fract("")`, (*FractionType)(nil), errors.New(`bad syntax`)},
|
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||||
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)},
|
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||||
|
/* 22 */ {`string(1|2)`, "1|2", nil},
|
||||||
}
|
}
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-8
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,30 +16,37 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
||||||
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
||||||
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
/* 6 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)},
|
/* 6 */ {`@x="hello"; @x`, nil, `[1:3] variable references are not allowed in top level expressions: "@x"`},
|
||||||
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
||||||
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
||||||
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, `undefined variable or function "x"`},
|
||||||
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
||||||
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
|
||||||
/* 14 */ {`two=func(){2}; two(123)`, nil, errors.New(`two(): too much params -- expected 0, got 1`)},
|
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too many params -- expected 0, got 1`},
|
||||||
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
|
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
|
||||||
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, errors.New(`[1:16] can't mix default and non-default parameters`)},
|
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
|
||||||
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
|
||||||
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
|
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
|
||||||
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
|
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
|
||||||
|
/* 20 */ {`f=func(a,b){a*2+b}; f(1,10)`, int64(12), nil},
|
||||||
|
/* 21 */ {`f=func(a,b){a*2+b}; f(a=2,b=1)`, int64(5), nil},
|
||||||
|
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
|
||||||
|
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
|
||||||
|
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
|
||||||
|
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
|
||||||
|
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
||||||
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 17)
|
//runTestSuiteSpec(t, section, inputs, 19)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
|
func dummy(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-39
@@ -5,50 +5,51 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func subtract(ctx ExprContext, name string, args []any) (result any, err error) {
|
// TODO The new function param model does not allow this kind of test
|
||||||
if len(args) != 2 {
|
// ------------------------------------------------------------------
|
||||||
err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
// func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
return
|
// if len(args) != 2 {
|
||||||
}
|
// err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
||||||
x, xok := args[0].(int64)
|
// return
|
||||||
y, yok := args[1].(int64)
|
// }
|
||||||
if xok && yok {
|
// x, xok := args[0].(int64)
|
||||||
result = x - y
|
// y, yok := args[1].(int64)
|
||||||
} else {
|
// if xok && yok {
|
||||||
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
// result = x - y
|
||||||
}
|
// } else {
|
||||||
return
|
// err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
||||||
}
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func TestEvalStringA(t *testing.T) {
|
// func TestEvalStringA(t *testing.T) {
|
||||||
|
|
||||||
source := `a + b * subtract(4,2)`
|
// source := `a + b * subtract(4,2)`
|
||||||
args := []Arg{
|
// args := []Arg{
|
||||||
{"a", uint8(1)},
|
// {"a", uint8(1)},
|
||||||
{"b", int8(2)},
|
// {"b", int8(2)},
|
||||||
{"subtract", FuncTemplate(subtract)},
|
// {"subtract", FuncTemplate2(subtract)},
|
||||||
// force coverage
|
// // force coverage
|
||||||
{"a16", uint16(1)},
|
// {"a16", uint16(1)},
|
||||||
{"b16", int16(2)},
|
// {"b16", int16(2)},
|
||||||
{"a32", uint32(1)},
|
// {"a32", uint32(1)},
|
||||||
{"b32", int32(2)},
|
// {"b32", int32(2)},
|
||||||
{"a64", uint64(1)},
|
// {"a64", uint64(1)},
|
||||||
{"b64", int64(2)},
|
// {"b64", int64(2)},
|
||||||
{"f32", float32(1.0)},
|
// {"f32", float32(1.0)},
|
||||||
{"f64", float64(1.0)},
|
// {"f64", float64(1.0)},
|
||||||
}
|
// }
|
||||||
|
|
||||||
wantResult := int64(5)
|
// wantResult := int64(5)
|
||||||
gotResult, gotErr := EvalStringA(source, args...)
|
// gotResult, gotErr := EvalStringA(source, args...)
|
||||||
if value, ok := gotResult.(int64); ok && value != wantResult {
|
// if value, ok := gotResult.(int64); ok && value != wantResult {
|
||||||
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
// t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||||
t.Errorf("Error: %v", gotErr)
|
// t.Errorf("Error: %v", gotErr)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestEvalString(t *testing.T) {
|
func TestEvalString(t *testing.T) {
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ func TestEvalString(t *testing.T) {
|
|||||||
|
|
||||||
// force coverage
|
// force coverage
|
||||||
ctx.GetFuncInfo("dummy")
|
ctx.GetFuncInfo("dummy")
|
||||||
ctx.Call("dummy", []any{})
|
ctx.Call("dummy", map[string]any{})
|
||||||
|
|
||||||
source := `a + b * f`
|
source := `a + b * f`
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ func TestCollections(t *testing.T) {
|
|||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, errors.New(`[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`)},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|||||||
+7
-7
@@ -15,7 +15,7 @@ func TestNewListIterator(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "b" {
|
} else if item != "b" {
|
||||||
t.Errorf("expcted %q, got %q", "b", item)
|
t.Errorf("expected %q, got %q", "b", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ func TestNewListIterator2(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "d" {
|
} else if item != "d" {
|
||||||
t.Errorf("expcted %q, got %q", "d", item)
|
t.Errorf("expected %q, got %q", "d", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ func TestNewListIterator3(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "b" {
|
} else if item != "b" {
|
||||||
t.Errorf("expcted %q, got %q", "b", item)
|
t.Errorf("expected %q, got %q", "b", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ func TestNewIterList2(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "a" {
|
} else if item != "a" {
|
||||||
t.Errorf("expcted %q, got %q", "a", item)
|
t.Errorf("expected %q, got %q", "a", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ func TestNewIterList3(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "a" {
|
} else if item != "a" {
|
||||||
t.Errorf("expcted %q, got %q", "a", item)
|
t.Errorf("expected %q, got %q", "a", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func TestNewIterList5(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "123" {
|
} else if item != "123" {
|
||||||
t.Errorf("expcted %q, got %q", "123", item)
|
t.Errorf("expected %q, got %q", "123", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
@@ -96,7 +96,7 @@ func TestNewIterList6(t *testing.T) {
|
|||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "a" {
|
} else if item != "a" {
|
||||||
t.Errorf("expcted %q, got %q", "a", item)
|
t.Errorf("expected %q, got %q", "a", item)
|
||||||
} else {
|
} else {
|
||||||
t.Logf("Next: %v", item)
|
t.Logf("Next: %v", item)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-9
@@ -7,20 +7,26 @@ package expr
|
|||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
|
section := "Iterator"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
|
||||||
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||||
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
|
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
|
||||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
|
||||||
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||||
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
|
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
|
||||||
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
||||||
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||||
|
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
|
||||||
|
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
|
||||||
|
/* 13 */ {`it=$([1,2,3,4],1,3); it++; it++;`, int64(3), nil},
|
||||||
|
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
|
||||||
|
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
|
||||||
|
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
||||||
}
|
}
|
||||||
// inputs1 := []inputType{
|
|
||||||
// /* 1 */ {`0?{}`, nil, nil},
|
// runTestSuiteSpec(t, section, inputs, 11)
|
||||||
// }
|
runTestSuite(t, section, inputs)
|
||||||
runTestSuite(t, "Iterator", inputs)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-12
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,14 +21,14 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||||
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||||
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
/* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
|
||||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||||
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
||||||
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
||||||
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
||||||
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, errors.New(`[1:34] index 10 out of bounds`)},
|
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`},
|
||||||
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||||
@@ -37,18 +36,18 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
||||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)},
|
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, errors.New(`[1:18] infix operator "<<" requires two non-nil operands, got 1`)},
|
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||||
/* 28 */ {`2 << 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`)},
|
/* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
||||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, errors.New(`[1:6] infix operator ">>" requires two non-nil operands, got 0`)},
|
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||||
/* 30 */ {`2 >> 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`)},
|
/* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
||||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||||
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||||
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, errors.New(`index 5 out of bounds (0, 1)`)},
|
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||||
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, errors.New(`[1:12] index/key specification expected, got [] [list]`)},
|
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||||
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, errors.New(`[1:12] index/key is nil`)},
|
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
|
||||||
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||||
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
||||||
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
||||||
@@ -57,6 +56,6 @@ func TestListParser(t *testing.T) {
|
|||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 1)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// t_operator_test.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOperator(t *testing.T) {
|
||||||
|
section := "Operator"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
|
||||||
|
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
|
||||||
|
/* 3 */ {`f=func(){3}; unset "f()"`, int64(1), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
//runTestSuiteSpec(t, section, inputs, 3)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
+36
-31
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,13 +48,13 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 34 */ {`(((1)))`, int64(1), nil},
|
/* 34 */ {`(((1)))`, int64(1), nil},
|
||||||
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
|
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
|
||||||
/* 36 */ {`0 || 0.0 && "hello"`, false, nil},
|
/* 36 */ {`0 || 0.0 && "hello"`, false, nil},
|
||||||
/* 37 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)},
|
/* 37 */ {`"s" + true`, nil, `[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`},
|
||||||
/* 38 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)},
|
/* 38 */ {`+false`, nil, `[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`},
|
||||||
/* 39 */ {`false // very simple expression`, false, nil},
|
/* 39 */ {`false // very simple expression`, false, nil},
|
||||||
/* 40 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)},
|
/* 40 */ {`1 + // Missing right operator`, nil, `[1:4] infix operator "+" requires two non-nil operands, got 1`},
|
||||||
/* 41 */ {"", nil, nil},
|
/* 41 */ {"", nil, nil},
|
||||||
/* 42 */ {"4!", int64(24), nil},
|
/* 42 */ {"4!", int64(24), nil},
|
||||||
/* 43 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)},
|
/* 43 */ {"(-4)!", nil, `factorial of a negative integer (-4) is not allowed`},
|
||||||
/* 44 */ {"-4!", int64(-24), nil},
|
/* 44 */ {"-4!", int64(-24), nil},
|
||||||
/* 45 */ {"1.5 < 7", true, nil},
|
/* 45 */ {"1.5 < 7", true, nil},
|
||||||
/* 46 */ {"1.5 > 7", false, nil},
|
/* 46 */ {"1.5 > 7", false, nil},
|
||||||
@@ -67,20 +66,20 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 52 */ {`"1.5" > "7"`, false, nil},
|
/* 52 */ {`"1.5" > "7"`, false, nil},
|
||||||
/* 53 */ {`"1.5" == "7"`, false, nil},
|
/* 53 */ {`"1.5" == "7"`, false, nil},
|
||||||
/* 54 */ {`"1.5" != "7"`, true, nil},
|
/* 54 */ {`"1.5" != "7"`, true, nil},
|
||||||
/* 55 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)},
|
/* 55 */ {"1.5 < ", nil, `[1:6] infix operator "<" requires two non-nil operands, got 1`},
|
||||||
/* 56 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)},
|
/* 56 */ {"1.5 > ", nil, `[1:6] infix operator ">" requires two non-nil operands, got 1`},
|
||||||
/* 57 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)},
|
/* 57 */ {"1.5 <= ", nil, `[1:6] infix operator "<=" requires two non-nil operands, got 1`},
|
||||||
/* 58 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)},
|
/* 58 */ {"1.5 >= ", nil, `[1:6] infix operator ">=" requires two non-nil operands, got 1`},
|
||||||
/* 59 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)},
|
/* 59 */ {"1.5 != ", nil, `[1:6] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 60 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)},
|
/* 60 */ {"1.5 == ", nil, `[1:6] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 61 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)},
|
/* 61 */ {`"1.5" < `, nil, `[1:8] infix operator "<" requires two non-nil operands, got 1`},
|
||||||
/* 62 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)},
|
/* 62 */ {`"1.5" > `, nil, `[1:8] infix operator ">" requires two non-nil operands, got 1`},
|
||||||
/* 63 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)},
|
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 64 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)},
|
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 66 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)},
|
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
||||||
/* 67 */ {"4 / 0", nil, errors.New(`division by zero`)},
|
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||||
/* 68 */ {"4.0 / 0", nil, errors.New(`division by zero`)},
|
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
/* 70 */ {`123`, int64(123), nil},
|
/* 70 */ {`123`, int64(123), nil},
|
||||||
/* 71 */ {`1.`, float64(1.0), nil},
|
/* 71 */ {`1.`, float64(1.0), nil},
|
||||||
@@ -92,7 +91,7 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 77 */ {`5 % 2`, int64(1), nil},
|
/* 77 */ {`5 % 2`, int64(1), nil},
|
||||||
/* 78 */ {`5 % (-2)`, int64(1), nil},
|
/* 78 */ {`5 % (-2)`, int64(1), nil},
|
||||||
/* 79 */ {`-5 % 2`, int64(-1), nil},
|
/* 79 */ {`-5 % 2`, int64(-1), nil},
|
||||||
/* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`)},
|
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
||||||
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
||||||
@@ -105,12 +104,12 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 90 */ {`x=2 but x*10`, int64(20), nil},
|
/* 90 */ {`x=2 but x*10`, int64(20), nil},
|
||||||
/* 91 */ {`false and true`, false, nil},
|
/* 91 */ {`false and true`, false, nil},
|
||||||
/* 92 */ {`false and (x==2)`, false, nil},
|
/* 92 */ {`false and (x==2)`, false, nil},
|
||||||
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, `undefined variable or function "x"`},
|
||||||
/* 94 */ {`false or true`, true, nil},
|
/* 94 */ {`false or true`, true, nil},
|
||||||
/* 95 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 95 */ {`false or (x==2)`, nil, `undefined variable or function "x"`},
|
||||||
/* 96 */ {`a=5; a`, int64(5), nil},
|
/* 96 */ {`a=5; a`, int64(5), nil},
|
||||||
/* 97 */ {`2=5`, nil, errors.New(`[1:2] left operand of "=" must be a variable or a collection's item`)},
|
/* 97 */ {`2=5`, nil, `[1:2] left operand of "=" must be a variable or a collection's item`},
|
||||||
/* 98 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable or a collection's item`)},
|
/* 98 */ {`2+a=5`, nil, `[1:3] left operand of "=" must be a variable or a collection's item`},
|
||||||
/* 99 */ {`2+(a=5)`, int64(7), nil},
|
/* 99 */ {`2+(a=5)`, int64(7), nil},
|
||||||
/* 100 */ {`x ?? "default"`, "default", nil},
|
/* 100 */ {`x ?? "default"`, "default", nil},
|
||||||
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
||||||
@@ -120,25 +119,31 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
|
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
|
||||||
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
|
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
|
||||||
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
|
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
|
||||||
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)},
|
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, `[1:34] case list in default clause`},
|
||||||
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
|
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
|
||||||
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
|
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
|
||||||
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)},
|
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, `[1:3] no case catches the value (10) of the selection expression`},
|
||||||
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)},
|
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, `[1:22] selector-case outside of a selector context`},
|
||||||
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
||||||
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
||||||
/* 115 */ {`nil`, nil, nil},
|
/* 115 */ {`nil`, nil, nil},
|
||||||
/* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
|
/* 116 */ {`null`, nil, `undefined variable or function "null"`},
|
||||||
/* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
|
/* 117 */ {`{"key"}`, nil, "[1:8] expected `:`, got `}`"},
|
||||||
/* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)},
|
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||||
/* 119 */ {`{}`, &DictType{}, nil},
|
/* 119 */ {`{}`, &DictType{}, nil},
|
||||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
||||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
||||||
/* 123 */ {`1|(2-2)`, nil, errors.New(`division by zero`)},
|
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
||||||
|
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||||
|
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||||
|
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
||||||
|
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||||
|
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||||
|
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
// parserTestSpec(t, section, inputs, 102)
|
//runTestSuiteSpec(t, section, inputs, 130)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-2
@@ -9,8 +9,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func _TestImportPlugin(t *testing.T) {
|
func _TestImportPlugin(t *testing.T) {
|
||||||
if err := importPlugin([]string{"test-resources"}, "json"); err != nil {
|
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||||
t.Errorf("importPlugin() failed: %v", err)
|
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||||
|
|
||||||
|
gotCount, gotErr := importPluginFromSearchPath("json")
|
||||||
|
if gotCount != 1 {
|
||||||
|
t.Errorf("Import count: got=%d, want=1", gotCount)
|
||||||
|
}
|
||||||
|
if gotErr != nil {
|
||||||
|
t.Errorf("importPlugin() failed: %v", gotErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -6,7 +6,6 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -69,9 +68,9 @@ func TestScanner(t *testing.T) {
|
|||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
if input.wantErr == nil {
|
if input.wantErr == nil {
|
||||||
t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source))
|
t.Logf("[+]Test nr %2d -- %q", i+1, input.source)
|
||||||
} else {
|
} else {
|
||||||
t.Log(fmt.Sprintf("[-]Test nr %2d -- %q", i+1, input.source))
|
t.Logf("[-]Test nr %2d -- %q", i+1, input.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const (
|
|||||||
priSign
|
priSign
|
||||||
priFact
|
priFact
|
||||||
priIterValue
|
priIterValue
|
||||||
priCoalesce
|
priDefault
|
||||||
priIncDec
|
priIncDec
|
||||||
priDot
|
priDot
|
||||||
priValue
|
priValue
|
||||||
@@ -53,21 +53,40 @@ type term struct {
|
|||||||
evalFunc evalFuncType
|
evalFunc evalFuncType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) String() string {
|
func (s *term) Clone() (d *term) {
|
||||||
|
var children []*term
|
||||||
|
if s.children != nil {
|
||||||
|
children = make([]*term, len(s.children))
|
||||||
|
for i, c := range s.children {
|
||||||
|
children[i] = c.Clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d = &term{
|
||||||
|
tk: *s.tk.Clone(),
|
||||||
|
parent: s.parent,
|
||||||
|
children: children,
|
||||||
|
position: s.position,
|
||||||
|
priority: s.priority,
|
||||||
|
evalFunc: s.evalFunc,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (term *term) String() string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
self.toString(&sb)
|
term.toString(&sb)
|
||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) toString(sb *strings.Builder) {
|
func (term *term) toString(sb *strings.Builder) {
|
||||||
if self.position == posLeaf {
|
if term.position == posLeaf {
|
||||||
sb.WriteString(self.tk.String())
|
sb.WriteString(term.tk.String())
|
||||||
} else {
|
} else {
|
||||||
sb.WriteByte('[')
|
sb.WriteByte('[')
|
||||||
sb.WriteString(self.tk.String())
|
sb.WriteString(term.tk.String())
|
||||||
if self.children != nil {
|
if term.children != nil {
|
||||||
sb.WriteByte('(')
|
sb.WriteByte('(')
|
||||||
for i, c := range self.children {
|
for i, c := range term.children {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
sb.WriteByte(' ')
|
sb.WriteByte(' ')
|
||||||
}
|
}
|
||||||
@@ -79,17 +98,17 @@ func (self *term) toString(sb *strings.Builder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) getChildrenCount() (count int) {
|
func (term *term) getChildrenCount() (count int) {
|
||||||
if self.position == posLeaf || self.children == nil {
|
if term.position == posLeaf || term.children == nil {
|
||||||
count = 0
|
count = 0
|
||||||
} else {
|
} else {
|
||||||
count = len(self.children)
|
count = len(term.children)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) getRoom() (room int) {
|
func (term *term) getRoom() (room int) {
|
||||||
switch self.position {
|
switch term.position {
|
||||||
case posLeaf:
|
case posLeaf:
|
||||||
room = 0
|
room = 0
|
||||||
case posInfix:
|
case posInfix:
|
||||||
@@ -102,139 +121,139 @@ func (self *term) getRoom() (room int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) isComplete() bool {
|
func (term *term) isComplete() bool {
|
||||||
return self.getChildrenCount() == self.getRoom()
|
return term.getChildrenCount() == term.getRoom()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) removeLastChild() (child *term) {
|
func (term *term) removeLastChild() (child *term) {
|
||||||
if self.children != nil {
|
if term.children != nil {
|
||||||
child = self.children[len(self.children)-1]
|
child = term.children[len(term.children)-1]
|
||||||
self.children = self.children[0 : len(self.children)-1]
|
term.children = term.children[0 : len(term.children)-1]
|
||||||
} else {
|
} else {
|
||||||
panic("Can't get last child")
|
panic("Can't get last child")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) isLeaf() bool {
|
func (term *term) isLeaf() bool {
|
||||||
return self.position == posLeaf
|
return term.position == posLeaf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) getPriority() termPriority {
|
func (term *term) getPriority() termPriority {
|
||||||
return self.priority
|
return term.priority
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) setParent(parent *term) {
|
func (term *term) setParent(parent *term) {
|
||||||
self.parent = parent
|
term.parent = parent
|
||||||
if parent != nil && len(parent.children) < cap(parent.children) {
|
if parent != nil && len(parent.children) < cap(parent.children) {
|
||||||
parent.children = append(parent.children, self)
|
parent.children = append(parent.children, term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) symbol() Symbol {
|
func (term *term) symbol() Symbol {
|
||||||
return self.tk.Sym
|
return term.tk.Sym
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) source() string {
|
func (term *term) source() string {
|
||||||
return self.tk.source
|
return term.tk.source
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) value() any {
|
func (term *term) value() any {
|
||||||
return self.tk.Value
|
return term.tk.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) compute(ctx ExprContext) (v any, err error) {
|
func (term *term) compute(ctx ExprContext) (v any, err error) {
|
||||||
if self.evalFunc == nil {
|
if term.evalFunc == nil {
|
||||||
err = self.tk.Errorf("undefined eval-func for %q term", self.source())
|
err = term.Errorf("undefined eval-func for %q term", term.source())
|
||||||
} else {
|
} else {
|
||||||
v, err = self.evalFunc(ctx, self)
|
v, err = term.evalFunc(ctx, term)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) {
|
// func (term *term) toInt(computedValue any, valueDescription string) (i int, err error) {
|
||||||
if index64, ok := computedValue.(int64); ok {
|
// if index64, ok := computedValue.(int64); ok {
|
||||||
i = int(index64)
|
// i = int(index64)
|
||||||
} else {
|
// } else {
|
||||||
err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
|
// err = term.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
func (term *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||||
leftType := TypeName(leftValue)
|
leftType := TypeName(leftValue)
|
||||||
leftText := getFormatted(leftValue, Truncate)
|
leftText := getFormatted(leftValue, Truncate)
|
||||||
rightType := TypeName(rightValue)
|
rightType := TypeName(rightValue)
|
||||||
rightText := getFormatted(rightValue, Truncate)
|
rightText := getFormatted(rightValue, Truncate)
|
||||||
return self.tk.Errorf(
|
return term.tk.Errorf(
|
||||||
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
|
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
|
||||||
leftText, leftType,
|
leftText, leftType,
|
||||||
rightText, rightType,
|
rightText, rightType,
|
||||||
self.source())
|
term.source())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleType(value any) error {
|
func (term *term) errIncompatibleType(value any) error {
|
||||||
return self.tk.Errorf(
|
return term.tk.Errorf(
|
||||||
"prefix/postfix operator %q do not support operand '%v' [%s]",
|
"prefix/postfix operator %q do not support operand '%v' [%s]",
|
||||||
self.source(), value, TypeName(value))
|
term.source(), value, TypeName(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) Errorf(template string, args ...any) (err error) {
|
func (term *term) Errorf(template string, args ...any) (err error) {
|
||||||
err = self.tk.Errorf(template, args...)
|
err = term.tk.Errorf(template, args...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) checkOperands() (err error) {
|
func (term *term) checkOperands() (err error) {
|
||||||
switch self.position {
|
switch term.position {
|
||||||
case posInfix:
|
case posInfix:
|
||||||
if self.children == nil || len(self.children) != 2 || self.anyChildrenNil() {
|
if term.children == nil || len(term.children) != 2 || term.anyChildrenNil() {
|
||||||
err = self.tk.Errorf("infix operator %q requires two non-nil operands, got %d", self.source(), self.getChildrenCount())
|
err = term.tk.Errorf("infix operator %q requires two non-nil operands, got %d", term.source(), term.getChildrenCount())
|
||||||
}
|
}
|
||||||
case posPrefix:
|
case posPrefix:
|
||||||
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||||
err = self.tk.Errorf("prefix operator %q requires one not nil operand", self.tk.String())
|
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posPostfix:
|
case posPostfix:
|
||||||
if self.children == nil || len(self.children) != 1 || self.anyChildrenNil() {
|
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||||
err = self.tk.Errorf("postfix operator %q requires one not nil operand", self.tk.String())
|
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posMultifix:
|
case posMultifix:
|
||||||
if self.children == nil || len(self.children) < 3 || self.anyChildrenNil() {
|
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||||
err = self.tk.Errorf("infix operator %q requires at least three not operands, got %d", self.source(), self.getChildrenCount())
|
err = term.tk.Errorf("infix operator %q requires at least three not operands, got %d", term.source(), term.getChildrenCount())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) anyChildrenNil() bool {
|
func (term *term) anyChildrenNil() bool {
|
||||||
for _, child := range self.children {
|
for _, child := range term.children {
|
||||||
if child == nil {
|
if child == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func (self *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) {
|
func (term *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) {
|
||||||
if err = self.checkOperands(); err == nil {
|
if err = term.checkOperands(); err == nil {
|
||||||
if leftValue, err = self.children[0].compute(ctx); err == nil {
|
if leftValue, err = term.children[0].compute(ctx); err == nil {
|
||||||
rightValue, err = self.children[1].compute(ctx)
|
rightValue, err = term.children[1].compute(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
|
func (term *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
|
||||||
if err = self.checkOperands(); err == nil {
|
if err = term.checkOperands(); err == nil {
|
||||||
childValue, err = self.children[0].compute(ctx)
|
childValue, err = term.children[0].compute(ctx)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE Temporary solution to support function parameters with default value
|
// NOTE Temporary solution to support function parameters with default value
|
||||||
|
|
||||||
func (self *term) forceChild(c *term) {
|
func (t *term) forceChild(c *term) {
|
||||||
if self.children == nil {
|
if t.children == nil {
|
||||||
self.children = make([]*term, 0, 1)
|
t.children = make([]*term, 0, 1)
|
||||||
}
|
}
|
||||||
self.children = append(self.children, c)
|
t.children = append(t.children, c)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
ds={
|
||||||
|
"init":func(end){@end=end; @current=0 but true},
|
||||||
|
"current":func(){current},
|
||||||
|
"next":func(){
|
||||||
|
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
|
||||||
|
}
|
||||||
|
, "filter":func(item,index) { item > 0 }
|
||||||
|
, "map": func(item,index) { item * 2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Example
|
||||||
|
;
|
||||||
|
it=$(ds,3);
|
||||||
|
add(it)
|
||||||
|
*/
|
||||||
@@ -28,12 +28,12 @@ func (tk *Token) DevString() string {
|
|||||||
func (tk *Token) String() string {
|
func (tk *Token) String() string {
|
||||||
if tk.Value != nil {
|
if tk.Value != nil {
|
||||||
if s, ok := tk.Value.(string); ok {
|
if s, ok := tk.Value.(string); ok {
|
||||||
return fmt.Sprintf("%q", s)
|
return s //fmt.Sprintf("%q", s)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Sprintf("%v", tk.Value)
|
return fmt.Sprintf("%v", tk.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s", tk.source)
|
return tk.source
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewToken(row, col int, sym Symbol, source string) *Token {
|
func NewToken(row, col int, sym Symbol, source string) *Token {
|
||||||
@@ -51,6 +51,10 @@ func NewErrorToken(row, col int, err error) *Token {
|
|||||||
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) Clone() (c *Token) {
|
||||||
|
return NewValueToken(tk.row, tk.col, tk.Sym, tk.source, tk.Value)
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) IsEos() bool {
|
func (tk *Token) IsEos() bool {
|
||||||
return tk.Sym == SymEos
|
return tk.Sym == SymEos
|
||||||
}
|
}
|
||||||
@@ -67,35 +71,39 @@ func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
|
|||||||
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
|
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) IsOneOfA(termSymbols ...Symbol) bool {
|
||||||
|
return slices.Index(termSymbols, tk.Sym) >= 0
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) IsSymbol(sym Symbol) bool {
|
func (tk *Token) IsSymbol(sym Symbol) bool {
|
||||||
return tk.Sym == sym
|
return tk.Sym == sym
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Token) Errorf(template string, args ...any) (err error) {
|
func (tk *Token) Errorf(template string, args ...any) (err error) {
|
||||||
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...)
|
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Token) Error() (err error) {
|
func (tk *Token) Error() (err error) {
|
||||||
if self.Sym == SymError {
|
if tk.Sym == SymError {
|
||||||
if msg, ok := self.Value.(error); ok {
|
if msg, ok := tk.Value.(error); ok {
|
||||||
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Token) Errors(msg string) (err error) {
|
func (tk *Token) Errors(msg string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Token) ErrorExpectedGot(symbol string) (err error) {
|
func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self)
|
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, tk)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got)
|
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsString(v any) (ok bool) {
|
func IsString(v any) (ok bool) {
|
||||||
@@ -221,3 +225,37 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExpandPath(sourcePath string) (expandedPath string, err error) {
|
||||||
|
for expandedPath = os.ExpandEnv(sourcePath); expandedPath != sourcePath; expandedPath = os.ExpandEnv(sourcePath) {
|
||||||
|
sourcePath = expandedPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(sourcePath, "~") {
|
||||||
|
var home, userName, remainder string
|
||||||
|
|
||||||
|
slashPos := strings.IndexRune(sourcePath, '/')
|
||||||
|
if slashPos > 0 {
|
||||||
|
userName = sourcePath[1:slashPos]
|
||||||
|
remainder = sourcePath[slashPos:]
|
||||||
|
} else {
|
||||||
|
userName = sourcePath[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(userName) == 0 {
|
||||||
|
home, err = os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var userInfo *user.User
|
||||||
|
userInfo, err = user.Lookup(userName)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
home = userInfo.HomeDir
|
||||||
|
}
|
||||||
|
expandedPath = path.Join(home, remainder)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user