config/lang: use the new AST stuff
This commit is contained in:
parent
c96b3b9ddc
commit
57adfe53f6
|
@ -8,16 +8,16 @@ import (
|
||||||
|
|
||||||
// NOTE: All builtins are tested in engine_test.go
|
// NOTE: All builtins are tested in engine_test.go
|
||||||
|
|
||||||
func registerBuiltins(scope *Scope) {
|
func registerBuiltins(scope *ast.BasicScope) {
|
||||||
if scope.FuncMap == nil {
|
if scope.FuncMap == nil {
|
||||||
scope.FuncMap = make(map[string]Function)
|
scope.FuncMap = make(map[string]ast.Function)
|
||||||
}
|
}
|
||||||
scope.FuncMap["__builtin_IntToString"] = builtinIntToString()
|
scope.FuncMap["__builtin_IntToString"] = builtinIntToString()
|
||||||
scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt()
|
scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinIntToString() Function {
|
func builtinIntToString() ast.Function {
|
||||||
return Function{
|
return ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeInt},
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
Callback: func(args []interface{}) (interface{}, error) {
|
||||||
|
@ -26,8 +26,8 @@ func builtinIntToString() Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func builtinStringToInt() Function {
|
func builtinStringToInt() ast.Function {
|
||||||
return Function{
|
return ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeInt},
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
Callback: func(args []interface{}) (interface{}, error) {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
// resolve properly and that the right number of arguments are passed
|
// resolve properly and that the right number of arguments are passed
|
||||||
// to functions.
|
// to functions.
|
||||||
type IdentifierCheck struct {
|
type IdentifierCheck struct {
|
||||||
Scope *Scope
|
Scope ast.Scope
|
||||||
|
|
||||||
err error
|
err error
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
|
|
|
@ -9,20 +9,20 @@ import (
|
||||||
func TestIdentifierCheck(t *testing.T) {
|
func TestIdentifierCheck(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input string
|
Input string
|
||||||
Scope *Scope
|
Scope ast.Scope
|
||||||
Error bool
|
Error bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
&Scope{},
|
&ast.BasicScope{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar} success",
|
"foo ${bar} success",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
Type: ast.TypeString,
|
Type: ast.TypeString,
|
||||||
},
|
},
|
||||||
|
@ -33,15 +33,15 @@ func TestIdentifierCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{},
|
&ast.BasicScope{},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()} success",
|
"foo ${rand()} success",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
return "42", nil
|
return "42", nil
|
||||||
|
@ -54,15 +54,15 @@ func TestIdentifierCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()}",
|
"foo ${rand()}",
|
||||||
&Scope{},
|
&ast.BasicScope{},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand(42)} ",
|
"foo ${rand(42)} ",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
return "42", nil
|
return "42", nil
|
||||||
|
@ -75,9 +75,9 @@ func TestIdentifierCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()} ",
|
"foo ${rand()} ",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
VariadicType: ast.TypeInt,
|
VariadicType: ast.TypeInt,
|
||||||
|
@ -92,9 +92,9 @@ func TestIdentifierCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand(42)} ",
|
"foo ${rand(42)} ",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
VariadicType: ast.TypeInt,
|
VariadicType: ast.TypeInt,
|
||||||
|
@ -109,9 +109,9 @@ func TestIdentifierCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand(\"foo\", 42)} ",
|
"foo ${rand(\"foo\", 42)} ",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
|
|
|
@ -16,7 +16,7 @@ import (
|
||||||
// this structure but we'd rather do that than duplicate the type checking
|
// this structure but we'd rather do that than duplicate the type checking
|
||||||
// logic multiple times.
|
// logic multiple times.
|
||||||
type TypeCheck struct {
|
type TypeCheck struct {
|
||||||
Scope *Scope
|
Scope ast.Scope
|
||||||
|
|
||||||
// Implicit is a map of implicit type conversions that we can do,
|
// Implicit is a map of implicit type conversions that we can do,
|
||||||
// and that shouldn't error. The key of the first map is the from type,
|
// and that shouldn't error. The key of the first map is the from type,
|
||||||
|
@ -140,7 +140,7 @@ func (v *TypeCheck) visitConcat(n *ast.Concat) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TypeCheck) visitLiteral(n *ast.LiteralNode) {
|
func (v *TypeCheck) visitLiteral(n *ast.LiteralNode) {
|
||||||
v.stackPush(n.Type)
|
v.stackPush(n.Typex)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *TypeCheck) visitVariableAccess(n *ast.VariableAccess) {
|
func (v *TypeCheck) visitVariableAccess(n *ast.VariableAccess) {
|
||||||
|
|
|
@ -9,20 +9,20 @@ import (
|
||||||
func TestTypeCheck(t *testing.T) {
|
func TestTypeCheck(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input string
|
Input string
|
||||||
Scope *Scope
|
Scope ast.Scope
|
||||||
Error bool
|
Error bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
&Scope{},
|
&ast.BasicScope{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
Type: ast.TypeString,
|
Type: ast.TypeString,
|
||||||
},
|
},
|
||||||
|
@ -33,9 +33,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()}",
|
"foo ${rand()}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
return "42", nil
|
return "42", nil
|
||||||
|
@ -48,9 +48,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand("42")}`,
|
`foo ${rand("42")}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
|
@ -64,9 +64,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand(42)}`,
|
`foo ${rand(42)}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
|
@ -80,9 +80,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand()}`,
|
`foo ${rand()}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: nil,
|
ArgTypes: nil,
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
|
@ -98,9 +98,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand("42")}`,
|
`foo ${rand("42")}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: nil,
|
ArgTypes: nil,
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
|
@ -116,9 +116,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand("42", 42)}`,
|
`foo ${rand("42", 42)}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ArgTypes: nil,
|
ArgTypes: nil,
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
|
@ -134,9 +134,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: 42,
|
Value: 42,
|
||||||
Type: ast.TypeInt,
|
Type: ast.TypeInt,
|
||||||
},
|
},
|
||||||
|
@ -147,9 +147,9 @@ func TestTypeCheck(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()}",
|
"foo ${rand()}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeInt,
|
ReturnType: ast.TypeInt,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
return 42, nil
|
return 42, nil
|
||||||
|
@ -184,14 +184,14 @@ func TestTypeCheck_implicit(t *testing.T) {
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input string
|
Input string
|
||||||
Scope *Scope
|
Scope *ast.BasicScope
|
||||||
Error bool
|
Error bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: 42,
|
Value: 42,
|
||||||
Type: ast.TypeInt,
|
Type: ast.TypeInt,
|
||||||
},
|
},
|
||||||
|
@ -202,9 +202,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${foo(42)}",
|
"foo ${foo(42)}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"foo": Function{
|
"foo": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
},
|
},
|
||||||
|
@ -215,9 +215,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${foo("42", 42)}`,
|
`foo ${foo("42", 42)}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"foo": Function{
|
"foo": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeString},
|
ArgTypes: []ast.Type{ast.TypeString},
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
VariadicType: ast.TypeString,
|
VariadicType: ast.TypeString,
|
||||||
|
@ -237,9 +237,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
||||||
|
|
||||||
// Modify the scope to add our conversion functions.
|
// Modify the scope to add our conversion functions.
|
||||||
if tc.Scope.FuncMap == nil {
|
if tc.Scope.FuncMap == nil {
|
||||||
tc.Scope.FuncMap = make(map[string]Function)
|
tc.Scope.FuncMap = make(map[string]ast.Function)
|
||||||
}
|
}
|
||||||
tc.Scope.FuncMap["intToString"] = Function{
|
tc.Scope.FuncMap["intToString"] = ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeInt},
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// prior to running Execute.
|
// prior to running Execute.
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
// GlobalScope is the global scope of execution for this engine.
|
// GlobalScope is the global scope of execution for this engine.
|
||||||
GlobalScope *Scope
|
GlobalScope *ast.BasicScope
|
||||||
|
|
||||||
// SemanticChecks is a list of additional semantic checks that will be run
|
// SemanticChecks is a list of additional semantic checks that will be run
|
||||||
// on the tree prior to executing it. The type checker, identifier checker,
|
// on the tree prior to executing it. The type checker, identifier checker,
|
||||||
|
@ -61,8 +61,8 @@ func (e *Engine) Execute(root ast.Node) (interface{}, ast.Type, error) {
|
||||||
return v.Visit(root)
|
return v.Visit(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) scope() *Scope {
|
func (e *Engine) scope() ast.Scope {
|
||||||
var scope Scope
|
var scope ast.BasicScope
|
||||||
if e.GlobalScope != nil {
|
if e.GlobalScope != nil {
|
||||||
scope = *e.GlobalScope
|
scope = *e.GlobalScope
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func (e *Engine) scope() *Scope {
|
||||||
// a program. Note at this point it is assumed that the types check out
|
// a program. Note at this point it is assumed that the types check out
|
||||||
// and the identifiers exist.
|
// and the identifiers exist.
|
||||||
type executeVisitor struct {
|
type executeVisitor struct {
|
||||||
Scope *Scope
|
Scope ast.Scope
|
||||||
|
|
||||||
stack EngineStack
|
stack EngineStack
|
||||||
err error
|
err error
|
||||||
|
@ -102,7 +102,12 @@ func (v *executeVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
|
||||||
v.stack.Reset()
|
v.stack.Reset()
|
||||||
v.err = nil
|
v.err = nil
|
||||||
|
|
||||||
return result.Value, result.Type, resultErr
|
t, err := result.Type(v.Scope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ast.TypeInvalid, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Value, t, resultErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *executeVisitor) visit(raw ast.Node) ast.Node {
|
func (v *executeVisitor) visit(raw ast.Node) ast.Node {
|
||||||
|
@ -151,7 +156,7 @@ func (v *executeVisitor) visitCall(n *ast.Call) {
|
||||||
// Push the result
|
// Push the result
|
||||||
v.stack.Push(&ast.LiteralNode{
|
v.stack.Push(&ast.LiteralNode{
|
||||||
Value: result,
|
Value: result,
|
||||||
Type: function.ReturnType,
|
Typex: function.ReturnType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +175,7 @@ func (v *executeVisitor) visitConcat(n *ast.Concat) {
|
||||||
|
|
||||||
v.stack.Push(&ast.LiteralNode{
|
v.stack.Push(&ast.LiteralNode{
|
||||||
Value: buf.String(),
|
Value: buf.String(),
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +193,7 @@ func (v *executeVisitor) visitVariableAccess(n *ast.VariableAccess) {
|
||||||
|
|
||||||
v.stack.Push(&ast.LiteralNode{
|
v.stack.Push(&ast.LiteralNode{
|
||||||
Value: variable.Value,
|
Value: variable.Value,
|
||||||
Type: variable.Type,
|
Typex: variable.Type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,33 +223,3 @@ func (s *EngineStack) Pop() *ast.LiteralNode {
|
||||||
func (s *EngineStack) Reset() {
|
func (s *EngineStack) Reset() {
|
||||||
s.stack = nil
|
s.stack = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scope represents a lookup scope for execution.
|
|
||||||
type Scope struct {
|
|
||||||
// VarMap and FuncMap are the mappings of identifiers to functions
|
|
||||||
// and variable values.
|
|
||||||
VarMap map[string]Variable
|
|
||||||
FuncMap map[string]Function
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupFunc will look up a variable by name.
|
|
||||||
// TODO test
|
|
||||||
func (s *Scope) LookupFunc(n string) (Function, bool) {
|
|
||||||
if s == nil {
|
|
||||||
return Function{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := s.FuncMap[n]
|
|
||||||
return v, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// LookupVar will look up a variable by name.
|
|
||||||
// TODO test
|
|
||||||
func (s *Scope) LookupVar(n string) (Variable, bool) {
|
|
||||||
if s == nil {
|
|
||||||
return Variable{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
v, ok := s.VarMap[n]
|
|
||||||
return v, ok
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func TestEngineExecute(t *testing.T) {
|
func TestEngineExecute(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Input string
|
Input string
|
||||||
Scope *Scope
|
Scope *ast.BasicScope
|
||||||
Error bool
|
Error bool
|
||||||
Result interface{}
|
Result interface{}
|
||||||
ResultType ast.Type
|
ResultType ast.Type
|
||||||
|
@ -26,9 +26,9 @@ func TestEngineExecute(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: "baz",
|
Value: "baz",
|
||||||
Type: ast.TypeString,
|
Type: ast.TypeString,
|
||||||
},
|
},
|
||||||
|
@ -41,9 +41,9 @@ func TestEngineExecute(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${rand()}",
|
"foo ${rand()}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func([]interface{}) (interface{}, error) {
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
return "42", nil
|
return "42", nil
|
||||||
|
@ -58,9 +58,9 @@ func TestEngineExecute(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${rand("foo", "bar")}`,
|
`foo ${rand("foo", "bar")}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"rand": Function{
|
"rand": ast.Function{
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Variadic: true,
|
Variadic: true,
|
||||||
VariadicType: ast.TypeString,
|
VariadicType: ast.TypeString,
|
||||||
|
@ -83,9 +83,9 @@ func TestEngineExecute(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
"foo ${bar}",
|
"foo ${bar}",
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
VarMap: map[string]Variable{
|
VarMap: map[string]ast.Variable{
|
||||||
"bar": Variable{
|
"bar": ast.Variable{
|
||||||
Value: 42,
|
Value: 42,
|
||||||
Type: ast.TypeInt,
|
Type: ast.TypeInt,
|
||||||
},
|
},
|
||||||
|
@ -98,9 +98,9 @@ func TestEngineExecute(t *testing.T) {
|
||||||
|
|
||||||
{
|
{
|
||||||
`foo ${foo("42")}`,
|
`foo ${foo("42")}`,
|
||||||
&Scope{
|
&ast.BasicScope{
|
||||||
FuncMap: map[string]Function{
|
FuncMap: map[string]ast.Function{
|
||||||
"foo": Function{
|
"foo": ast.Function{
|
||||||
ArgTypes: []ast.Type{ast.TypeInt},
|
ArgTypes: []ast.Type{ast.TypeInt},
|
||||||
ReturnType: ast.TypeString,
|
ReturnType: ast.TypeString,
|
||||||
Callback: func(args []interface{}) (interface{}, error) {
|
Callback: func(args []interface{}) (interface{}, error) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ top:
|
||||||
{
|
{
|
||||||
parserResult = &ast.LiteralNode{
|
parserResult = &ast.LiteralNode{
|
||||||
Value: "",
|
Value: "",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ top:
|
||||||
// it makes for an easy literal check later (to check if a string
|
// it makes for an easy literal check later (to check if a string
|
||||||
// has any interpolations).
|
// has any interpolations).
|
||||||
if _, ok := $1.(*ast.Concat); !ok {
|
if _, ok := $1.(*ast.Concat); !ok {
|
||||||
if n, ok := $1.(*ast.LiteralNode); !ok || n.Type != ast.TypeString {
|
if n, ok := $1.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString {
|
||||||
parserResult = &ast.Concat{
|
parserResult = &ast.Concat{
|
||||||
Exprs: []ast.Node{$1},
|
Exprs: []ast.Node{$1},
|
||||||
Posx: $1.Pos(),
|
Posx: $1.Pos(),
|
||||||
|
@ -104,7 +104,7 @@ expr:
|
||||||
{
|
{
|
||||||
$$ = &ast.LiteralNode{
|
$$ = &ast.LiteralNode{
|
||||||
Value: $1.Value.(int),
|
Value: $1.Value.(int),
|
||||||
Type: ast.TypeInt,
|
Typex: ast.TypeInt,
|
||||||
Posx: $1.Pos,
|
Posx: $1.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,7 @@ expr:
|
||||||
{
|
{
|
||||||
$$ = &ast.LiteralNode{
|
$$ = &ast.LiteralNode{
|
||||||
Value: $1.Value.(float64),
|
Value: $1.Value.(float64),
|
||||||
Type: ast.TypeFloat,
|
Typex: ast.TypeFloat,
|
||||||
Posx: $1.Pos,
|
Posx: $1.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ literal:
|
||||||
{
|
{
|
||||||
$$ = &ast.LiteralNode{
|
$$ = &ast.LiteralNode{
|
||||||
Value: $1.Value.(string),
|
Value: $1.Value.(string),
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: $1.Pos,
|
Posx: $1.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestParse(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "",
|
Value: "",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,7 @@ func TestParse(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo",
|
Value: "foo",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,7 @@ func TestParse(t *testing.T) {
|
||||||
false,
|
false,
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "${var.foo}",
|
Value: "${var.foo}",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -51,7 +51,7 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.VariableAccess{
|
&ast.VariableAccess{
|
||||||
|
@ -70,7 +70,7 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.VariableAccess{
|
&ast.VariableAccess{
|
||||||
|
@ -79,7 +79,7 @@ func TestParse(t *testing.T) {
|
||||||
},
|
},
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: " baz",
|
Value: " baz",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 15, Line: 1},
|
Posx: ast.Pos{Column: 15, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -94,12 +94,12 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "bar",
|
Value: "bar",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 7, Line: 1},
|
Posx: ast.Pos{Column: 7, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -114,12 +114,12 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: 42,
|
Value: 42,
|
||||||
Type: ast.TypeInt,
|
Typex: ast.TypeInt,
|
||||||
Posx: ast.Pos{Column: 7, Line: 1},
|
Posx: ast.Pos{Column: 7, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -134,12 +134,12 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: 3.14159,
|
Value: 3.14159,
|
||||||
Type: ast.TypeFloat,
|
Typex: ast.TypeFloat,
|
||||||
Posx: ast.Pos{Column: 7, Line: 1},
|
Posx: ast.Pos{Column: 7, Line: 1},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -239,7 +239,7 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "foo ",
|
Value: "foo ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.Concat{
|
&ast.Concat{
|
||||||
|
@ -247,7 +247,7 @@ func TestParse(t *testing.T) {
|
||||||
Exprs: []ast.Node{
|
Exprs: []ast.Node{
|
||||||
&ast.LiteralNode{
|
&ast.LiteralNode{
|
||||||
Value: "bar ",
|
Value: "bar ",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 7, Line: 1},
|
Posx: ast.Pos{Column: 7, Line: 1},
|
||||||
},
|
},
|
||||||
&ast.VariableAccess{
|
&ast.VariableAccess{
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
package lang
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/lang/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LookupType looks up the type of the given node with the given scope.
|
|
||||||
func LookupType(raw ast.Node, scope *Scope) (ast.Type, error) {
|
|
||||||
switch n := raw.(type) {
|
|
||||||
case *ast.LiteralNode:
|
|
||||||
return typedLiteralNode{n}.Type(scope)
|
|
||||||
case *ast.VariableAccess:
|
|
||||||
return typedVariableAccess{n}.Type(scope)
|
|
||||||
default:
|
|
||||||
if t, ok := raw.(TypedNode); ok {
|
|
||||||
return t.Type(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ast.TypeInvalid, fmt.Errorf(
|
|
||||||
"unknown node to get type of: %T", raw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypedNode is an interface that custom AST nodes should implement
|
|
||||||
// if they want to work with LookupType. All the builtin AST nodes have
|
|
||||||
// implementations of this.
|
|
||||||
type TypedNode interface {
|
|
||||||
Type(*Scope) (ast.Type, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type typedLiteralNode struct {
|
|
||||||
n *ast.LiteralNode
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n typedLiteralNode) Type(s *Scope) (ast.Type, error) {
|
|
||||||
return n.n.Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type typedVariableAccess struct {
|
|
||||||
n *ast.VariableAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n typedVariableAccess) Type(s *Scope) (ast.Type, error) {
|
|
||||||
v, ok := s.LookupVar(n.n.Name)
|
|
||||||
if !ok {
|
|
||||||
return ast.TypeInvalid, fmt.Errorf(
|
|
||||||
"%s: couldn't find variable %s", n.n.Pos(), n.n.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Type, nil
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
package lang
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config/lang/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLookupType(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Input ast.Node
|
|
||||||
Scope *Scope
|
|
||||||
Output ast.Type
|
|
||||||
Error bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
&customUntyped{},
|
|
||||||
nil,
|
|
||||||
ast.TypeInvalid,
|
|
||||||
true,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
&customTyped{},
|
|
||||||
nil,
|
|
||||||
ast.TypeString,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
&ast.LiteralNode{
|
|
||||||
Value: 42,
|
|
||||||
Type: ast.TypeInt,
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
ast.TypeInt,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
&ast.VariableAccess{
|
|
||||||
Name: "foo",
|
|
||||||
},
|
|
||||||
&Scope{
|
|
||||||
VarMap: map[string]Variable{
|
|
||||||
"foo": Variable{Type: ast.TypeInt},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ast.TypeInt,
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
actual, err := LookupType(tc.Input, tc.Scope)
|
|
||||||
if (err != nil) != tc.Error {
|
|
||||||
t.Fatalf("bad: %s\n\nInput: %#v", err, tc.Input)
|
|
||||||
}
|
|
||||||
if actual != tc.Output {
|
|
||||||
t.Fatalf("bad: %s\n\nInput: %#v", actual, tc.Input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type customUntyped struct{}
|
|
||||||
|
|
||||||
func (n customUntyped) Accept(ast.Visitor) ast.Node { return n }
|
|
||||||
func (n customUntyped) Pos() (v ast.Pos) { return }
|
|
||||||
|
|
||||||
type customTyped struct{}
|
|
||||||
|
|
||||||
func (n customTyped) Accept(ast.Visitor) ast.Node { return n }
|
|
||||||
func (n customTyped) Pos() (v ast.Pos) { return }
|
|
||||||
func (n customTyped) Type(*Scope) (ast.Type, error) { return ast.TypeString, nil }
|
|
|
@ -346,7 +346,7 @@ parserdefault:
|
||||||
{
|
{
|
||||||
parserResult = &ast.LiteralNode{
|
parserResult = &ast.LiteralNode{
|
||||||
Value: "",
|
Value: "",
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: ast.Pos{Column: 1, Line: 1},
|
Posx: ast.Pos{Column: 1, Line: 1},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ parserdefault:
|
||||||
// it makes for an easy literal check later (to check if a string
|
// it makes for an easy literal check later (to check if a string
|
||||||
// has any interpolations).
|
// has any interpolations).
|
||||||
if _, ok := parserS[parserpt-0].node.(*ast.Concat); !ok {
|
if _, ok := parserS[parserpt-0].node.(*ast.Concat); !ok {
|
||||||
if n, ok := parserS[parserpt-0].node.(*ast.LiteralNode); !ok || n.Type != ast.TypeString {
|
if n, ok := parserS[parserpt-0].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString {
|
||||||
parserResult = &ast.Concat{
|
parserResult = &ast.Concat{
|
||||||
Exprs: []ast.Node{parserS[parserpt-0].node},
|
Exprs: []ast.Node{parserS[parserpt-0].node},
|
||||||
Posx: parserS[parserpt-0].node.Pos(),
|
Posx: parserS[parserpt-0].node.Pos(),
|
||||||
|
@ -417,7 +417,7 @@ parserdefault:
|
||||||
{
|
{
|
||||||
parserVAL.node = &ast.LiteralNode{
|
parserVAL.node = &ast.LiteralNode{
|
||||||
Value: parserS[parserpt-0].token.Value.(int),
|
Value: parserS[parserpt-0].token.Value.(int),
|
||||||
Type: ast.TypeInt,
|
Typex: ast.TypeInt,
|
||||||
Posx: parserS[parserpt-0].token.Pos,
|
Posx: parserS[parserpt-0].token.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ parserdefault:
|
||||||
{
|
{
|
||||||
parserVAL.node = &ast.LiteralNode{
|
parserVAL.node = &ast.LiteralNode{
|
||||||
Value: parserS[parserpt-0].token.Value.(float64),
|
Value: parserS[parserpt-0].token.Value.(float64),
|
||||||
Type: ast.TypeFloat,
|
Typex: ast.TypeFloat,
|
||||||
Posx: parserS[parserpt-0].token.Pos,
|
Posx: parserS[parserpt-0].token.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ parserdefault:
|
||||||
{
|
{
|
||||||
parserVAL.node = &ast.LiteralNode{
|
parserVAL.node = &ast.LiteralNode{
|
||||||
Value: parserS[parserpt-0].token.Value.(string),
|
Value: parserS[parserpt-0].token.Value.(string),
|
||||||
Type: ast.TypeString,
|
Typex: ast.TypeString,
|
||||||
Posx: parserS[parserpt-0].token.Pos,
|
Posx: parserS[parserpt-0].token.Pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue