config/lang: function calls work
This commit is contained in:
parent
4ae8cae9e7
commit
8f925b93e0
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -75,6 +75,15 @@ func TestParse(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"${foo()}",
|
||||||
|
false,
|
||||||
|
&ast.Call{
|
||||||
|
Func: "foo",
|
||||||
|
Args: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"${foo(bar)}",
|
"${foo(bar)}",
|
||||||
false,
|
false,
|
||||||
|
|
Loading…
Reference in New Issue