diff --git a/ast.go b/ast.go index 6d1bb35..8aadfd4 100644 --- a/ast.go +++ b/ast.go @@ -10,11 +10,15 @@ import ( "strings" ) +type Expr interface { + Eval(ctx exprContext, preset bool) (result any, err error) +} + //-------- ast type ast struct { forest []*term - root *term + root *term } func NewAst() *ast { @@ -54,7 +58,7 @@ func (self *ast) addToken(tk *Token) (err error) { if t := newTerm(tk, nil); t != nil { err = self.addTerm(t) } else { - err = fmt.Errorf("No term constructor for token %q", tk.String()) + err = tk.Errorf("No term constructor for token %q", tk.String()) } return } @@ -83,23 +87,25 @@ func (self *ast) insert(tree, node *term) (root *term, err error) { root = node tree.setParent(node) } else { - err = fmt.Errorf("two adjacent operators: %q and %q", tree, node) + err = node.Errorf("two adjacent operators: %q and %q", tree, node) } return } func (self *ast) Finish() { - if self.root == nil && self.forest != nil && len(self.forest) >= 1 { - self.root = self.forest[len(self.forest)-1] - self.forest = self.forest[0:len(self.forest) - 1] - } + if self.root == nil && self.forest != nil && len(self.forest) >= 1 { + self.root = self.forest[len(self.forest)-1] + self.forest = self.forest[0 : len(self.forest)-1] + } } -func (self *ast) Eval(ctx exprContext) (result any, err error) { +func (self *ast) Eval(ctx exprContext, preset bool) (result any, err error) { self.Finish() if self.root != nil { - initDefaultVars(ctx) + if preset { + initDefaultVars(ctx) + } if self.forest != nil { for i, root := range self.forest { if result, err = root.compute(ctx); err == nil { @@ -121,7 +127,7 @@ func (self *ast) Eval(ctx exprContext) (result any, err error) { // Preset variables const ( - preset_last_result = "_last" + preset_last_result = "_last" preset_bool_shortcut = "_bool_shortcut" ) diff --git a/ast_test.go b/ast_test.go index 2e68234..67d0c2e 100644 --- a/ast_test.go +++ b/ast_test.go @@ -17,9 +17,9 @@ func TestAstString(t *testing.T) { } func TestAddTokensGood(t *testing.T) { - tk1 := NewValueToken(SymInteger, "100", 100) - tk2 := NewToken(SymPlus, "+") - tk3 := NewValueToken(SymInteger, "50", 500) + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) + tk2 := NewToken(0, 0, SymPlus, "+") + tk3 := NewValueToken(0, 0, SymInteger, "50", 500) tree := NewAst() if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr != nil { @@ -28,12 +28,12 @@ func TestAddTokensGood(t *testing.T) { } func TestAddTokensBad(t *testing.T) { - tk0 := NewValueToken(SymInteger, "200", 200) - tk1 := NewValueToken(SymInteger, "100", 100) - tk2 := NewToken(SymPlus, "+") - tk3 := NewValueToken(SymInteger, "50", 500) + tk0 := NewValueToken(0, 0, SymInteger, "200", 200) + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) + tk2 := NewToken(0, 0, SymPlus, "+") + tk3 := NewValueToken(0, 0, SymInteger, "50", 500) - wantErr := errors.New(`two adjacent operators: "200" and "100"`) + wantErr := errors.New(`[0:0] two adjacent operators: "200" and "100"`) tree := NewAst() if gotErr := tree.addTokens(tk0, tk1, tk2, tk3); gotErr != nil && gotErr.Error() != wantErr.Error() { @@ -42,7 +42,7 @@ func TestAddTokensBad(t *testing.T) { } func TestAddUnknownTokens(t *testing.T) { - tk0 := NewToken(SymPercent, "%") + tk0 := NewToken(0, 0, SymPercent, "%") wantErr := errors.New(`No term constructor for token "%"`) diff --git a/context.go b/context.go index 37fd1b9..7ae7fd4 100644 --- a/context.go +++ b/context.go @@ -4,18 +4,35 @@ // context.go package expr +// ---- Function template type FuncTemplate func(ctx exprContext, name string, args []any) (result any, err error) +// ---- Functor interface +type Functor interface { + Invoke(ctx exprContext, name string, args []any) (result any, err error) +} + +type simpleFunctor struct { + f FuncTemplate +} + +func (functor *simpleFunctor) Invoke(ctx exprContext, name string, args []any) (result any, err error) { + return functor.f(ctx, name, args) +} + +// ---- Function Info type exprFunc interface { Name() string MinArgs() int MaxArgs() int } +// ----Expression Context type exprContext interface { + Clone() exprContext GetValue(varName string) (value any, exists bool) SetValue(varName string, value any) GetFuncInfo(name string) exprFunc Call(name string, args []any) (result any, err error) - RegisterFunc(name string, f FuncTemplate, minArgs, maxArgs int) + RegisterFunc(name string, f Functor, minArgs, maxArgs int) } diff --git a/funcs-math.go b/funcs-math.go index 567adbd..121dd51 100644 --- a/funcs-math.go +++ b/funcs-math.go @@ -134,6 +134,6 @@ func mulFunc(ctx exprContext, name string, args []any) (result any, err error) { } func importMathFuncs(ctx exprContext) { - ctx.RegisterFunc("add", addFunc, 0, -1) - ctx.RegisterFunc("mul", mulFunc, 0, -1) + ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1) + ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1) } diff --git a/helpers.go b/helpers.go index 3fbdf68..1765974 100644 --- a/helpers.go +++ b/helpers.go @@ -14,7 +14,7 @@ func EvalString(ctx exprContext, source string) (result any, err error) { parser := NewParser(ctx) if tree, err = parser.parse(scanner); err == nil { - result, err = tree.Eval(ctx) + result, err = tree.Eval(ctx, true) } return } @@ -33,7 +33,8 @@ func EvalStringV(source string, args []EvalArg) (result any, err error) { for _, arg := range args { if isFunc(arg.value) { if f, ok := arg.value.(FuncTemplate); ok { - ctx.RegisterFunc(arg.name, f, 0, -1) + functor := &simpleFunctor{f: f} + ctx.RegisterFunc(arg.name, functor, 0, -1) } else { err = fmt.Errorf("invalid function specification: %q", arg.name) } diff --git a/operand-expr.go b/operand-expr.go new file mode 100644 index 0000000..ae433a4 --- /dev/null +++ b/operand-expr.go @@ -0,0 +1,36 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// operand-expr.go +package expr + +import "errors" + +// -------- expr term +func newExprTerm(tk *Token) *term { + return &term{ + tk: *tk, + class: classVar, + kind: kindUnknown, + parent: nil, + children: nil, + position: posLeaf, + priority: priValue, + evalFunc: evalExpr, + } +} + +// -------- eval expr +func evalExpr(ctx exprContext, self *term) (v any, err error) { + if expr, ok := self.value().(Expr); ok { + v, err = expr.Eval(ctx, false) + } else { + err = errors.New("invalid body of function definition") + } + return +} + +// init +// func init() { +// registerTermConstructor(SymExpression, newExprTerm) +// } diff --git a/operand-func.go b/operand-func.go index 6975c63..3e18afd 100644 --- a/operand-func.go +++ b/operand-func.go @@ -4,8 +4,13 @@ // operand-func.go package expr -// -------- function term -func newFuncTerm(tk *Token, args []*term) *term { +import ( + "errors" + "fmt" +) + +// -------- function call term +func newFuncCallTerm(tk *Token, args []*term) *term { return &term{ tk: *tk, class: classVar, @@ -14,12 +19,12 @@ func newFuncTerm(tk *Token, args []*term) *term { children: args, position: posLeaf, priority: priValue, - evalFunc: evalFunc, + evalFunc: evalFuncCall, } } -// -------- eval func -func evalFunc(ctx exprContext, self *term) (v any, err error) { +// -------- eval func call +func evalFuncCall(ctx exprContext, self *term) (v any, err error) { name, _ := self.tk.Value.(string) params := make([]any, len(self.children)) for i, tree := range self.children { @@ -34,3 +39,59 @@ func evalFunc(ctx exprContext, self *term) (v any, err error) { } return } + +// -------- function definition term +func newFuncDefTerm(tk *Token, args []*term) *term { + return &term{ + tk: *tk, + class: classVar, + kind: kindUnknown, + parent: nil, + children: args, // arg[0]=formal-param-list, arg[1]=*ast + position: posLeaf, + priority: priValue, + evalFunc: evalFuncDef, + } +} + +// -------- eval func def +// TODO +type funcDefFunctor struct { + params []string + expr Expr +} + +func (functor *funcDefFunctor) Invoke(parentCtx exprContext, name string, args []any) (result any, err error) { + ctx := parentCtx.Clone() + for i, p := range functor.params { + if i < len(args) { + ctx.SetValue(p, args[i]) + } else { + ctx.SetValue(p, nil) + } + } + result, err = functor.expr.Eval(ctx, false) + return +} + +func evalFuncDef(ctx exprContext, self *term) (v any, err error) { + bodySpec := self.value() + if expr, ok := bodySpec.(*ast); ok { + paramList := make([]string, 0, len(self.children)) + for i, param := range self.children { + if paramName, ok := param.value().(string); ok { + paramList = append(paramList, paramName) + } else { + err = fmt.Errorf("invalid function definition: formal param nr %d must be an identifiers", i+1) + break + } + } + v = &funcDefFunctor{ + params: paramList, + expr: expr, + } + } else { + err = errors.New("invalid function definition: the body specification must be an expression") + } + return +} diff --git a/operand-list.go b/operand-list.go index 9a52522..39bf8ef 100644 --- a/operand-list.go +++ b/operand-list.go @@ -7,7 +7,7 @@ package expr // -------- list term func newListTerm(args []*term) *term { return &term{ - tk: *NewToken(SymList, "[]"), + tk: *NewToken(0, 0, SymList, "[]"), class: classVar, kind: kindUnknown, parent: nil, diff --git a/operator-assign.go b/operator-assign.go index 76062ba..d6e3367 100644 --- a/operator-assign.go +++ b/operator-assign.go @@ -4,8 +4,6 @@ // operator-assign.go package expr -import "fmt" - //-------- assign term func newAssignTerm(tk *Token) (inst *term) { @@ -27,12 +25,16 @@ func evalAssign(ctx exprContext, self *term) (v any, err error) { leftTerm := self.children[0] if leftTerm.tk.Sym != SymIdentifier { - err = fmt.Errorf("left operand of %q must be a variable", self.tk.source) + err = leftTerm.tk.Errorf("left operand of %q must be a variable", self.tk.source) return } if v, err = self.children[1].compute(ctx); err == nil { - ctx.SetValue(leftTerm.tk.source, v) + if functor, ok := v.(Functor); ok { + ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) + } else { + ctx.SetValue(leftTerm.tk.source, v) + } } return } diff --git a/parser.go b/parser.go index ec7b127..a1a2ab7 100644 --- a/parser.go +++ b/parser.go @@ -5,6 +5,7 @@ package expr import ( + "errors" "fmt" ) @@ -21,22 +22,23 @@ func NewParser(ctx exprContext) (p *parser) { return p } -func (self *parser) parseFunction(scanner *scanner, tk *Token) (tree *term, err error) { - name, _ := tk.Value.(string) - funcObj := self.ctx.GetFuncInfo(name) - if funcObj == nil { - err = fmt.Errorf("unknown function %s()", name) - return - } - maxArgs := funcObj.MaxArgs() - if maxArgs < 0 { - maxArgs = funcObj.MinArgs() + 10 - } - args := make([]*term, 0, maxArgs) +func (self *parser) parseFuncCall(scanner *scanner, tk *Token) (tree *term, err error) { + // name, _ := tk.Value.(string) + // funcObj := self.ctx.GetFuncInfo(name) + // if funcObj == nil { + // err = fmt.Errorf("unknown function %s()", name) + // return + // } + // maxArgs := funcObj.MaxArgs() + // if maxArgs < 0 { + // maxArgs = funcObj.MinArgs() + 10 + // } + // args := make([]*term, 0, maxArgs) + args := make([]*term, 0, 10) lastSym := SymUnknown for lastSym != SymClosedRound && lastSym != SymEos { var subTree *ast - if subTree, err = self.parse(scanner, SymComma, SymClosedRound); err == nil { + if subTree, err = self.parseItem(scanner, SymComma, SymClosedRound); err == nil { if subTree.root != nil { args = append(args, subTree.root) } @@ -47,7 +49,47 @@ func (self *parser) parseFunction(scanner *scanner, tk *Token) (tree *term, err } if err == nil { // TODO Check arguments - tree = newFuncTerm(tk, args) + if lastSym != SymClosedRound { + err = errors.New("unterminate arguments list") + } else { + tree = newFuncCallTerm(tk, args) + } + } + return +} + +func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { + // Example: "add = func(x,y) {x+y} + var body *ast + args := make([]*term, 0) + tk := scanner.Next() + for tk.Sym != SymClosedRound && tk.Sym != SymEos { + if tk.Sym == SymIdentifier { + t := newTerm(tk, nil) + args = append(args, t) + } else { + err = tk.Errorf("invalid param %q, variable identifier expected", tk.source) + break + } + tk = scanner.Next() + } + if err == nil && tk.Sym != SymClosedRound { + err = tk.Errorf("unterminate function params list") + } + if err == nil { + tk = scanner.Next() + if tk.Sym == SymOpenBrace { + body, err = self.parseGeneral(scanner, true, SymClosedBrace) + } + } + if err == nil { + // TODO Check arguments + if scanner.Previous().Sym != SymClosedBrace { + err = scanner.Previous().Errorf("unterminate function body") + } else { + tk = scanner.makeValueToken(SymExpression, "", body) + tree = newFuncDefTerm(tk, args) + } } return } @@ -57,7 +99,7 @@ func (self *parser) parseList(scanner *scanner) (tree *term, err error) { lastSym := SymUnknown for lastSym != SymClosedSquare && lastSym != SymEos { var subTree *ast - if subTree, err = self.parse(scanner, SymComma, SymClosedSquare); err == nil { + if subTree, err = self.parseItem(scanner, SymComma, SymClosedSquare); err == nil { if subTree.root != nil { args = append(args, subTree.root) } @@ -68,12 +110,24 @@ func (self *parser) parseList(scanner *scanner) (tree *term, err error) { } if err == nil { // TODO Check arguments - tree = newListTerm(args) + if lastSym != SymClosedSquare { + err = scanner.Previous().Errorf("unterminate items list") + } else { + tree = newListTerm(args) + } } return } func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { + return self.parseGeneral(scanner, true, termSymbols...) +} + +func (self *parser) parseItem(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { + return self.parseGeneral(scanner, false, termSymbols...) +} + +func (self *parser) parseGeneral(scanner *scanner, allowForset bool, termSymbols ...Symbol) (tree *ast, err error) { tree = NewAst() firstToken := true lastSym := SymUnknown @@ -83,19 +137,25 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e } if tk.Sym == SymSemiColon { - tree.ToForest() - continue + if allowForset { + tree.ToForest() + continue + } else { + err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source) + break + } } //fmt.Println("Token:", tk) - if firstToken && (tk.Sym == SymMinus || tk.Sym == SymPlus) { + if firstToken { if tk.Sym == SymMinus { tk.Sym = SymChangeSign - } else { + } else if tk.Sym == SymPlus { tk.Sym = SymUnchangeSign } + firstToken = false } - firstToken = false + switch tk.Sym { case SymOpenRound: var subTree *ast @@ -103,10 +163,10 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e subTree.root.priority = priValue tree.addTerm(subTree.root) } - case SymFunction: - var funcTerm *term - if funcTerm, err = self.parseFunction(scanner, tk); err == nil { - err = tree.addTerm(funcTerm) + case SymFuncCall: + var funcCallTerm *term + if funcCallTerm, err = self.parseFuncCall(scanner, tk); err == nil { + err = tree.addTerm(funcCallTerm) } case SymOpenSquare: var listTerm *term @@ -114,10 +174,13 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e err = tree.addTerm(listTerm) } case SymEqual: - if lastSym == SymIdentifier { + if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil { err = tree.addToken(tk) - } else { - err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source) + } + case SymFuncDef: + var funcDefTerm *term + if funcDefTerm, err = self.parseFuncDef(scanner); err == nil { + err = tree.addTerm(funcDefTerm) } default: err = tree.addToken(tk) @@ -126,3 +189,10 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e } return } + +func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) { + if lastSym != wantedSym { + err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source) + } + return +} diff --git a/parser_test.go b/parser_test.go index 5a9a982..31f8649 100644 --- a/parser_test.go +++ b/parser_test.go @@ -18,12 +18,6 @@ func TestParser(t *testing.T) { wantErr error } - // inputs1 := []inputType{ - // {`a=5; a`, int64(5), nil}, - // {`a=5; b=2; add(a, b*3)`, int64(11), nil}, - // {`"a" < "b" AND ~ 2 == 1`, true, nil}, - // } - inputs := []inputType{ /* 1 */ {`1+/*5*/2`, int64(3), nil}, /* 2 */ {`3 == 4`, false, nil}, @@ -66,10 +60,10 @@ func TestParser(t *testing.T) { /* 39 */ {`(((1)))`, int64(1), nil}, /* 40 */ {`"uno_" + var2`, `uno_abc`, nil}, /* 41 */ {`0 || 0.0 && "hello"`, false, nil}, - /* 42 */ {`"s" + true`, nil, errors.New(`left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, - /* 43 */ {`+false`, nil, errors.New(`prefix/postfix operator "+" do not support operand 'false' [bool]`)}, + /* 42 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, + /* 43 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)}, /* 44 */ {`false // very simple expression`, false, nil}, - /* 45 */ {`1 + // Missing right operator`, nil, errors.New(`infix operator "+" requires two operands, got 1`)}, + /* 45 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two operands, got 1`)}, /* 46 */ {"", nil, errors.New(`empty expression`)}, /* 47 */ {"4!", int64(24), nil}, /* 48 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)}, @@ -84,18 +78,18 @@ func TestParser(t *testing.T) { /* 57 */ {`"1.5" > "7"`, false, nil}, /* 58 */ {`"1.5" == "7"`, false, nil}, /* 59 */ {`"1.5" != "7"`, true, nil}, - /* 60 */ {"1.5 < ", nil, errors.New(`infix operator "<" requires two operands, got 1`)}, - /* 61 */ {"1.5 > ", nil, errors.New(`infix operator ">" requires two operands, got 1`)}, - /* 62 */ {"1.5 <= ", nil, errors.New(`infix operator "<=" requires two operands, got 1`)}, - /* 63 */ {"1.5 >= ", nil, errors.New(`infix operator ">=" requires two operands, got 1`)}, - /* 64 */ {"1.5 != ", nil, errors.New(`infix operator "!=" requires two operands, got 1`)}, - /* 65 */ {"1.5 == ", nil, errors.New(`infix operator "==" requires two operands, got 1`)}, - /* 66 */ {`"1.5" < `, nil, errors.New(`infix operator "<" requires two operands, got 1`)}, - /* 67 */ {`"1.5" > `, nil, errors.New(`infix operator ">" requires two operands, got 1`)}, - /* 68 */ {`"1.5" == `, nil, errors.New(`infix operator "==" requires two operands, got 1`)}, - /* 69 */ {`"1.5" != `, nil, errors.New(`infix operator "!=" requires two operands, got 1`)}, + /* 60 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two operands, got 1`)}, + /* 61 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two operands, got 1`)}, + /* 62 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two operands, got 1`)}, + /* 63 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two operands, got 1`)}, + /* 64 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two operands, got 1`)}, + /* 65 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two operands, got 1`)}, + /* 66 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two operands, got 1`)}, + /* 67 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two operands, got 1`)}, + /* 68 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two operands, got 1`)}, + /* 69 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two operands, got 1`)}, /* 70 */ {"+1.5", float64(1.5), nil}, - /* 71 */ {"+", nil, errors.New(`prefix operator "+" requires one operand`)}, + /* 71 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one operand`)}, /* 72 */ {"4 / 0", nil, errors.New(`division by zero`)}, /* 73 */ {"4.0 / 0", nil, errors.New(`division by zero`)}, /* 74 */ {"4.0 / \n2", float64(2.0), nil}, @@ -109,7 +103,7 @@ func TestParser(t *testing.T) { /* 82 */ {`5 % 2`, int64(1), nil}, /* 83 */ {`5 % (-2)`, int64(1), nil}, /* 84 */ {`-5 % 2`, int64(-1), nil}, - /* 85 */ {`5 % 2.0`, nil, errors.New(`left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)}, + /* 85 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)}, /* 86 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, /* 87 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, /* 88 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, @@ -129,12 +123,24 @@ func TestParser(t *testing.T) { /* 102 */ {`a=5; a`, int64(5), nil}, /* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil}, /* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)}, - /* 105 */ {`2+a=5`, nil, errors.New(`left operand of "=" must be a variable`)}, + /* 105 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable`)}, /* 106 */ {`2+(a=5)`, int64(7), nil}, + /* 107 */ {`two=func(){2}; two()`, int64(2), nil}, + /* 108 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, + /* 109 */ {`double=func(x){2*x}; double(3)`, int64(6), nil}, + /* 110 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil}, + /* 111 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil}, } succeeded := 0 failed := 0 + // inputs1 := []inputType{ + // {`add(1,2,3)`, int64(6), nil}, + // {`a=5; a`, int64(5), nil}, + // {`a=5; b=2; add(a, b*3)`, int64(11), nil}, + // {`"a" < "b" AND ~ 2 == 1`, true, nil}, + // } + for i, input := range inputs { var expr *ast var gotResult any @@ -153,7 +159,7 @@ func TestParser(t *testing.T) { good := true if expr, gotErr = parser.parse(scanner); gotErr == nil { - gotResult, gotErr = expr.Eval(ctx) + gotResult, gotErr = expr.Eval(ctx, true) } if gotResult != input.wantResult { @@ -177,7 +183,7 @@ func TestParser(t *testing.T) { t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) } -func NoTestListParser(t *testing.T) { +func TestListParser(t *testing.T) { type inputType struct { source string wantResult any @@ -227,7 +233,7 @@ func NoTestListParser(t *testing.T) { good := true if expr, gotErr = parser.parse(scanner); gotErr == nil { - gotResult, gotErr = expr.Eval(ctx) + gotResult, gotErr = expr.Eval(ctx, true) } if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) { diff --git a/scanner.go b/scanner.go index 4dd3b95..b45a542 100644 --- a/scanner.go +++ b/scanner.go @@ -229,8 +229,12 @@ func (self *scanner) fetchNextToken() (tk *Token) { } escape = false default: - if ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { - tk = self.fetchIdentifier(ch) + if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { + if tk = self.fetchIdentifier(ch); tk.Sym == SymKwFunc { + if next, _ := self.peek(); next == '(' { + tk = self.moveOn(SymFuncDef, ch, next) + } + } } else if ch >= '0' && ch <= '9' { tk = self.parseNumber(ch) } @@ -337,7 +341,7 @@ func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) { tk = self.makeValueToken(SymBool, txt, false) } else if ch, _ := self.peek(); ch == '(' { self.readChar() - tk = self.makeValueToken(SymFunction, txt+"(", txt) + tk = self.makeValueToken(SymFuncCall, txt+"(", txt) } else { tk = self.makeValueToken(SymIdentifier, txt, txt) } @@ -533,7 +537,7 @@ func (self *scanner) translate(sym Symbol) Symbol { } func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { - tk = NewToken(self.translate(sym), string(chars)) + tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) for i := 1; i < len(chars); i++ { self.readChar() } @@ -541,17 +545,17 @@ func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { } func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) { - tk = NewToken(self.translate(sym), string(chars)) + tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) return } func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) { - tk = NewToken(self.translate(sym), upperCaseKeyword) + tk = NewToken(self.row, self.column, self.translate(sym), upperCaseKeyword) return } func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) { - tk = NewValueToken(self.translate(sym), source, value) + tk = NewValueToken(self.row, self.column, self.translate(sym), source, value) return } diff --git a/simple-func-store.go b/simple-func-store.go index cfd1d6e..a55c509 100644 --- a/simple-func-store.go +++ b/simple-func-store.go @@ -5,7 +5,7 @@ import "fmt" type SimpleFuncStore struct { varStore map[string]any - funcStore map[string]FuncTemplate + funcStore map[string]Functor } type funcInfo struct { @@ -29,7 +29,14 @@ func (info *funcInfo) MaxArgs() int { func NewSimpleFuncStore() *SimpleFuncStore { return &SimpleFuncStore{ varStore: make(map[string]any), - funcStore: make(map[string]FuncTemplate), + funcStore: make(map[string]Functor), + } +} + +func (ctx *SimpleFuncStore) Clone() exprContext { + return &SimpleFuncStore{ + varStore: CloneMap(ctx.varStore), + funcStore: CloneMap(ctx.funcStore), } } @@ -50,13 +57,13 @@ func (ctx *SimpleFuncStore) GetFuncInfo(name string) (f exprFunc) { return } -func (ctx *SimpleFuncStore) RegisterFunc(name string, f FuncTemplate, minArgs, maxArgs int) { - ctx.funcStore[name] = f +func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) { + ctx.funcStore[name] = functor } func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) { - if f, exists := ctx.funcStore[name]; exists { - result, err = f(ctx, name, args) + if functor, exists := ctx.funcStore[name]; exists { + result, err = functor.Invoke(ctx, name, args) } else { err = fmt.Errorf("unknown function %s()", name) } diff --git a/simple-var-store.go b/simple-var-store.go index ae89a36..613ec9c 100644 --- a/simple-var-store.go +++ b/simple-var-store.go @@ -11,6 +11,13 @@ func NewSimpleVarStore() *SimpleVarStore { } } +func (ctx *SimpleVarStore) Clone() (clone exprContext) { + clone = &SimpleVarStore{ + store: CloneMap(ctx.store), + } + return clone +} + func (ctx *SimpleVarStore) GetValue(varName string) (v any, exists bool) { v, exists = ctx.store[varName] return @@ -28,5 +35,5 @@ func (ctx *SimpleVarStore) Call(name string, args []any) (result any, err error) return } -func (ctx *SimpleVarStore) RegisterFunc(name string, f FuncTemplate, minArgs, maxArgs int) { +func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) { } diff --git a/symbol.go b/symbol.go index 308bde1..d003a70 100644 --- a/symbol.go +++ b/symbol.go @@ -69,9 +69,12 @@ const ( SymAnd SymNot SymComment - SymFunction + SymFuncCall + SymFuncDef SymList SymKwBut + SymKwFunc + SymExpression // SymOpenComment // 0: '/*' // SymClosedComment // 0: '*/' // SymOneLineComment // 0: '//' @@ -82,9 +85,10 @@ var keywords map[string]Symbol func init() { //keywords = make(map[string]Symbol) keywords = map[string]Symbol{ - "AND": SymKwAnd, - "BUT": SymKwBut, - "NOT": SymKwNot, - "OR": SymKwOr, + "AND": SymKwAnd, + "BUT": SymKwBut, + "FUNC": SymKwFunc, + "NOT": SymKwNot, + "OR": SymKwOr, } } diff --git a/term.go b/term.go index 7703cef..52e0082 100644 --- a/term.go +++ b/term.go @@ -5,7 +5,6 @@ package expr import ( - "fmt" "strings" ) @@ -146,14 +145,6 @@ func (self *term) isLeaf() bool { return self.position == posLeaf } -// func (self *term) getKind() termKind { -// return self.kind -// } - -// func (self *term) getClass() termClass { -// return self.class -// } - func (self *term) getPriority() termPriority { return self.priority } @@ -165,21 +156,17 @@ func (self *term) setParent(parent *term) { } } -// func (self *term) isOperand() bool { -// return self.getClass() != classOperator -// } - -// func (self *term) isOperator() bool { -// return self.getClass() == classOperator -// } - func (self *term) source() string { return self.tk.source } +func (self *term) value() any { + return self.tk.Value +} + func (self *term) compute(ctx exprContext) (v any, err error) { if self.evalFunc == nil { - err = fmt.Errorf("undefined eval-func for %v term type", self.kind) + err = self.tk.Errorf("undefined eval-func for %v term type", self.kind) } else { v, err = self.evalFunc(ctx, self) } @@ -187,7 +174,7 @@ func (self *term) compute(ctx exprContext) (v any, err error) { } func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { - return fmt.Errorf( + return self.tk.Errorf( "left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q", leftValue, leftValue, rightValue, rightValue, @@ -195,23 +182,29 @@ func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { } func (self *term) errIncompatibleType(value any) error { - return fmt.Errorf( - "prefix/postfix operator %q do not support operand '%v' [%T]", self.source(), value, value) + return self.tk.Errorf( + "prefix/postfix operator %q do not support operand '%v' [%T]", + self.source(), value, value) +} + +func (self *term) Errorf(template string, args ...any) (err error) { + err = self.tk.Errorf(template, args...) + return } func (self *term) checkOperands() (err error) { switch self.position { case posInfix: if self.children == nil || len(self.children) != 2 || self.children[0] == nil || self.children[1] == nil { - err = fmt.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount()) + err = self.tk.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount()) } case posPrefix: if self.children == nil || len(self.children) != 1 || self.children[0] == nil { - err = fmt.Errorf("prefix operator %q requires one operand", self.tk.String()) + err = self.tk.Errorf("prefix operator %q requires one operand", self.tk.String()) } case posPostfix: if self.children == nil || len(self.children) != 1 || self.children[0] == nil { - err = fmt.Errorf("postfix operator %q requires one operand", self.tk.String()) + err = self.tk.Errorf("postfix operator %q requires one operand", self.tk.String()) } } return diff --git a/term_test.go b/term_test.go index ee023ae..8bcfd76 100644 --- a/term_test.go +++ b/term_test.go @@ -10,9 +10,9 @@ import ( ) func TestString(t *testing.T) { - tk1 := NewValueToken(SymInteger, "100", 100) - tk2 := NewToken(SymPlus, "+") - tk3 := NewValueToken(SymInteger, "50", 500) + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) + tk2 := NewToken(0, 0, SymPlus, "+") + tk3 := NewValueToken(0, 0, SymInteger, "50", 500) tree := NewAst() if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil { @@ -23,7 +23,7 @@ func TestString(t *testing.T) { } func TestGetRoom(t *testing.T) { - tk1 := NewValueToken(SymInteger, "100", 100) + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) tree := NewAst() if gotErr := tree.addTokens(tk1); gotErr == nil { @@ -33,7 +33,7 @@ func TestGetRoom(t *testing.T) { } } func TestGetChildrenCount(t *testing.T) { - tk1 := NewValueToken(SymInteger, "100", 100) + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) tree := NewAst() if gotErr := tree.addTokens(tk1); gotErr == nil { diff --git a/token.go b/token.go index a159168..cd6ec3f 100644 --- a/token.go +++ b/token.go @@ -11,6 +11,8 @@ import ( ) type Token struct { + row int + col int Sym Symbol source string Value any @@ -30,19 +32,19 @@ func (tk *Token) String() string { return fmt.Sprintf("%s", tk.source) } -func NewToken(sym Symbol, source string) *Token { - return &Token{Sym: sym, source: source} +func NewToken(row, col int, sym Symbol, source string) *Token { + return &Token{row: row, col: col, Sym: sym, source: source} } -func NewValueToken(sym Symbol, source string, value any) *Token { - return &Token{Sym: sym, source: source, Value: value} +func NewValueToken(row, col int, sym Symbol, source string, value any) *Token { + return &Token{row: row, col: col, Sym: sym, source: source, Value: value} } func NewErrorToken(row, col int, err error) *Token { if err == io.EOF { - return NewToken(SymEos, "") + return NewToken(row, col, SymEos, "") } - return NewValueToken(SymError, fmt.Sprintf("[%d:%d]", row, col), err) + return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) } func (tk *Token) IsEos() bool { @@ -56,3 +58,8 @@ func (tk *Token) IsError() bool { func (tk *Token) IsTerm(termSymbols []Symbol) bool { return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0) } + +func (self *Token) Errorf(template string, args ...any) (err error) { + err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) + return +} diff --git a/token_test.go b/token_test.go index 0e58d20..35aa0bc 100644 --- a/token_test.go +++ b/token_test.go @@ -10,8 +10,8 @@ import ( ) func TestDevString(t *testing.T) { - tk1 := NewValueToken(SymInteger, "100", 100) - tk2 := NewToken(SymPlus, "+") + tk1 := NewValueToken(0, 0, SymInteger, "100", 100) + tk2 := NewToken(0, 0, SymPlus, "+") fmt.Println("Token '100':", tk1.DevString()) fmt.Println("Token '+':", tk2.DevString()) diff --git a/utils.go b/utils.go index 8989670..ca7e314 100644 --- a/utils.go +++ b/utils.go @@ -101,3 +101,15 @@ func anyFloat(v any) (float float64, ok bool) { } return } + +func CopyMap[K comparable, V any](dest, source map[K]V) map[K]V { + for k, v := range source { + dest[k] = v + } + return dest +} + +func CloneMap[K comparable, V any](source map[K]V) map[K]V { + dest := make(map[K]V, len(source)) + return CopyMap(dest, source) +}