config/lang: variadic functions
This commit is contained in:
parent
4af4c9e16c
commit
8d51b6b1d4
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue