config/lang: variadic functions

This commit is contained in:
Mitchell Hashimoto 2015-01-13 12:40:47 -08:00
parent 4af4c9e16c
commit 8d51b6b1d4
7 changed files with 168 additions and 3 deletions

View File

@ -33,7 +33,7 @@ type Visitor func(Node)
//go:generate stringer -type=Type
// Type is the type of a literal.
type Type uint
type Type uint32
const (
TypeInvalid Type = 0

View File

@ -52,8 +52,14 @@ func (c *IdentifierCheck) visitCall(n *ast.Call) {
return
}
// Break up the args into what is variadic and what is required
args := n.Args
if function.Variadic && len(args) > len(function.ArgTypes) {
args = n.Args[:len(function.ArgTypes)]
}
// Verify the number of arguments
if len(n.Args) != len(function.ArgTypes) {
if len(args) != len(function.ArgTypes) {
c.createErr(n, fmt.Sprintf(
"%s: expected %d arguments, got %d",
n.Func, len(function.ArgTypes), len(n.Args)))

View File

@ -72,6 +72,58 @@ func TestIdentifierCheck(t *testing.T) {
},
true,
},
{
"foo ${rand()} ",
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeInt,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
false,
},
{
"foo ${rand(42)} ",
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeInt,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
false,
},
{
"foo ${rand(\"foo\", 42)} ",
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ArgTypes: []ast.Type{ast.TypeString},
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeInt,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
false,
},
}
for _, tc := range cases {

View File

@ -68,6 +68,20 @@ func (v *TypeCheck) visitCall(n *ast.Call) {
}
}
// If we're variadic, then verify the types there
if function.Variadic {
args = args[len(function.ArgTypes):]
for i, t := range args {
if t != function.VariadicType {
v.createErr(n, fmt.Sprintf(
"%s: argument %d should be %s, got %s",
n.Func, i+len(function.ArgTypes),
function.VariadicType, t))
return
}
}
}
// Return type
v.stackPush(function.ReturnType)
}

View File

@ -78,6 +78,60 @@ func TestTypeCheck(t *testing.T) {
true,
},
{
`foo ${rand()}`,
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ArgTypes: nil,
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
false,
},
{
`foo ${rand("42")}`,
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ArgTypes: nil,
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
false,
},
{
`foo ${rand("42", 42)}`,
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ArgTypes: nil,
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func([]interface{}) (interface{}, error) {
return "42", nil
},
},
},
},
true,
},
{
"foo ${bar}",
&Scope{

View File

@ -215,9 +215,25 @@ type Variable struct {
// The type checker will validate that the proper types will be called
// to the callback.
type Function struct {
// ArgTypes is the list of types in argument order. These are the
// required arguments.
//
// ReturnType is the type of the returned value. The Callback MUST
// return this type.
ArgTypes []ast.Type
ReturnType ast.Type
Callback func([]interface{}) (interface{}, error)
// Variadic, if true, says that this function is variadic, meaning
// it takes a variable number of arguments. In this case, the
// VariadicType must be set.
Variadic bool
VariadicType ast.Type
// Callback is the function called for a function. The argument
// types are guaranteed to match the spec above by the type checker.
// The length of the args is strictly == len(ArgTypes) unless Varidiac
// is true, in which case its >= len(ArgTypes).
Callback func([]interface{}) (interface{}, error)
}
// LookupFunc will look up a variable by name.

View File

@ -54,6 +54,29 @@ func TestEngineExecute(t *testing.T) {
"foo 42",
ast.TypeString,
},
{
`foo ${rand("foo", "bar")}`,
&Scope{
FuncMap: map[string]Function{
"rand": Function{
ReturnType: ast.TypeString,
Variadic: true,
VariadicType: ast.TypeString,
Callback: func(args []interface{}) (interface{}, error) {
var result string
for _, a := range args {
result += a.(string)
}
return result, nil
},
},
},
},
false,
"foo foobar",
ast.TypeString,
},
}
for _, tc := range cases {