diff --git a/config/lang/engine.go b/config/lang/engine.go index 0fd8f7eac6..0b3a6c14d8 100644 --- a/config/lang/engine.go +++ b/config/lang/engine.go @@ -37,9 +37,9 @@ type Variable struct { // The type checker will validate that the proper types will be called // to the callback. type Function struct { - Name string - ArgTypes []ast.Type - Callback func([]interface{}) (interface{}, ast.Type, error) + ArgTypes []ast.Type + ReturnType ast.Type + Callback func([]interface{}) (interface{}, error) } // Execute executes the given ast.Node and returns its final value, its @@ -89,6 +89,8 @@ func (v *executeVisitor) visit(raw ast.Node) { } switch n := raw.(type) { + case *ast.Call: + v.visitCall(n) case *ast.Concat: v.visitConcat(n) case *ast.LiteralNode: @@ -100,6 +102,35 @@ func (v *executeVisitor) visit(raw ast.Node) { } } +func (v *executeVisitor) visitCall(n *ast.Call) { + // Look up the function in the map + function, ok := v.Engine.FuncMap[n.Func] + if !ok { + v.err = fmt.Errorf("unknown function called: %s", n.Func) + return + } + + // The arguments are on the stack in reverse order, so pop them off. + args := make([]interface{}, len(n.Args)) + for i, _ := range n.Args { + node := v.stackPop() + args[len(n.Args)-1-i] = node.Value + } + + // Call the function + result, err := function.Callback(args) + if err != nil { + v.err = fmt.Errorf("%s: %s", n.Func, err) + return + } + + // Push the result + v.stackPush(&ast.LiteralNode{ + Value: result, + Type: function.ReturnType, + }) +} + func (v *executeVisitor) visitConcat(n *ast.Concat) { // The expressions should all be on the stack in reverse // order. So pop them off, reverse their order, and concatenate. diff --git a/config/lang/engine_test.go b/config/lang/engine_test.go index 489758a6e8..96a1105d5a 100644 --- a/config/lang/engine_test.go +++ b/config/lang/engine_test.go @@ -37,6 +37,23 @@ func TestEngineExecute(t *testing.T) { "foo baz", ast.TypeString, }, + + { + "foo ${rand()}", + &Engine{ + FuncMap: map[string]Function{ + "rand": Function{ + ReturnType: ast.TypeString, + Callback: func([]interface{}) (interface{}, error) { + return "42", nil + }, + }, + }, + }, + false, + "foo 42", + ast.TypeString, + }, } for _, tc := range cases { diff --git a/config/lang/parse_test.go b/config/lang/parse_test.go index a635be8587..ec508b0e28 100644 --- a/config/lang/parse_test.go +++ b/config/lang/parse_test.go @@ -75,6 +75,15 @@ func TestParse(t *testing.T) { }, }, + { + "${foo()}", + false, + &ast.Call{ + Func: "foo", + Args: nil, + }, + }, + { "${foo(bar)}", false,