From 1d8569d3a991a430e18f61db5b20547312fb83d4 Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Sat, 4 May 2024 22:35:03 +0200 Subject: [PATCH] the function call procedure now check the number of actual parameters against the numer of formal parameters --- ast.go | 2 +- ast_test.go | 2 +- operand-func.go | 25 +++++++++++++++++++++---- operator-assign.go | 9 ++++++++- parser_test.go | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/ast.go b/ast.go index 5394d80..0534cc5 100644 --- a/ast.go +++ b/ast.go @@ -63,7 +63,7 @@ func (self *ast) addToken2(tk *Token) (t *term, err error) { if t = newTerm(tk, nil); t != nil { err = self.addTerm(t) } else { - err = tk.Errorf("No term constructor for token %q", tk.String()) + err = tk.Errorf("unexpected token %q", tk.String()) } return } diff --git a/ast_test.go b/ast_test.go index 67d0c2e..8b1a394 100644 --- a/ast_test.go +++ b/ast_test.go @@ -44,7 +44,7 @@ func TestAddTokensBad(t *testing.T) { func TestAddUnknownTokens(t *testing.T) { tk0 := NewToken(0, 0, SymPercent, "%") - wantErr := errors.New(`No term constructor for token "%"`) + wantErr := errors.New(`unexpected token "%"`) tree := NewAst() if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { diff --git a/operand-func.go b/operand-func.go index b0eba53..afac065 100644 --- a/operand-func.go +++ b/operand-func.go @@ -6,6 +6,7 @@ package expr import ( "errors" + "fmt" ) // -------- function call term @@ -21,6 +22,20 @@ func newFuncCallTerm(tk *Token, args []*term) *term { } // -------- eval func call +func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) { + if info, exists := ctx.GetFuncInfo(name); exists { + if info.MinArgs() > len(params) { + err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params)) + } + if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) { + err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params)) + } + } else { + err = fmt.Errorf("unknown function %s()", name) + } + return +} + func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { ctx := cloneContext(parentCtx) name, _ := self.tk.Value.(string) @@ -34,8 +49,10 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { params[i] = param } if err == nil { - if v, err = ctx.Call(name, params); err == nil { - exportObjects(parentCtx, ctx) + if err = checkFunctionCall(ctx, name, params); err == nil { + if v, err = ctx.Call(name, params); err == nil { + exportObjects(parentCtx, ctx) + } } } return @@ -44,9 +61,9 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { // -------- function definition term func newFuncDefTerm(tk *Token, args []*term) *term { return &term{ - tk: *tk, + tk: *tk, // value is the expression body parent: nil, - children: args, // arg[0]=formal-param-list, arg[1]=*ast + children: args, // function params position: posLeaf, priority: priValue, evalFunc: evalFuncDef, diff --git a/operator-assign.go b/operator-assign.go index 3d5e958..d03e06d 100644 --- a/operator-assign.go +++ b/operator-assign.go @@ -29,7 +29,14 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) { if v, err = self.children[1].compute(ctx); err == nil { if functor, ok := v.(Functor); ok { - ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) + var minArgs, maxArgs int = 0, 0 + + if funcDef, ok := functor.(*funcDefFunctor); ok { + l := len(funcDef.params) + minArgs = l + maxArgs = l + } + ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs) } else { ctx.setVar(leftTerm.source(), v) } diff --git a/parser_test.go b/parser_test.go index 11596dc..8ba0505 100644 --- a/parser_test.go +++ b/parser_test.go @@ -160,7 +160,7 @@ func TestParser(t *testing.T) { /* 138 */ {`nil`, nil, nil}, /* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, /* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, - /* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected dictionary value, got "}"`)}, + /* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, /* 142 */ {`{}`, map[any]any{}, nil}, /* 143 */ {`1|2`, newFraction(1, 2), nil}, /* 144 */ {`1|2 + 1`, newFraction(3, 2), nil},