config/lang: function calls work

This commit is contained in:
Mitchell Hashimoto 2015-01-11 15:33:24 -08:00
parent 4ae8cae9e7
commit 8f925b93e0
3 changed files with 60 additions and 3 deletions

View File

@ -37,9 +37,9 @@ type Variable struct {
// The type checker will validate that the proper types will be called // The type checker will validate that the proper types will be called
// to the callback. // to the callback.
type Function struct { type Function struct {
Name string ArgTypes []ast.Type
ArgTypes []ast.Type ReturnType ast.Type
Callback func([]interface{}) (interface{}, ast.Type, error) Callback func([]interface{}) (interface{}, error)
} }
// Execute executes the given ast.Node and returns its final value, its // 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) { switch n := raw.(type) {
case *ast.Call:
v.visitCall(n)
case *ast.Concat: case *ast.Concat:
v.visitConcat(n) v.visitConcat(n)
case *ast.LiteralNode: 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) { func (v *executeVisitor) visitConcat(n *ast.Concat) {
// The expressions should all be on the stack in reverse // The expressions should all be on the stack in reverse
// order. So pop them off, reverse their order, and concatenate. // order. So pop them off, reverse their order, and concatenate.

View File

@ -37,6 +37,23 @@ func TestEngineExecute(t *testing.T) {
"foo baz", "foo baz",
ast.TypeString, 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 { for _, tc := range cases {

View File

@ -75,6 +75,15 @@ func TestParse(t *testing.T) {
}, },
}, },
{
"${foo()}",
false,
&ast.Call{
Func: "foo",
Args: nil,
},
},
{ {
"${foo(bar)}", "${foo(bar)}",
false, false,