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
|
||||
|
||||
func registerBuiltins(scope *Scope) {
|
||||
func registerBuiltins(scope *ast.BasicScope) {
|
||||
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_StringToInt"] = builtinStringToInt()
|
||||
}
|
||||
|
||||
func builtinIntToString() Function {
|
||||
return Function{
|
||||
func builtinIntToString() ast.Function {
|
||||
return ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeInt},
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func(args []interface{}) (interface{}, error) {
|
||||
|
@ -26,8 +26,8 @@ func builtinIntToString() Function {
|
|||
}
|
||||
}
|
||||
|
||||
func builtinStringToInt() Function {
|
||||
return Function{
|
||||
func builtinStringToInt() ast.Function {
|
||||
return ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeInt},
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func(args []interface{}) (interface{}, error) {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
// resolve properly and that the right number of arguments are passed
|
||||
// to functions.
|
||||
type IdentifierCheck struct {
|
||||
Scope *Scope
|
||||
Scope ast.Scope
|
||||
|
||||
err error
|
||||
lock sync.Mutex
|
||||
|
|
|
@ -9,20 +9,20 @@ import (
|
|||
func TestIdentifierCheck(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Scope *Scope
|
||||
Scope ast.Scope
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
"foo",
|
||||
&Scope{},
|
||||
&ast.BasicScope{},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"foo ${bar} success",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: "baz",
|
||||
Type: ast.TypeString,
|
||||
},
|
||||
|
@ -33,15 +33,15 @@ func TestIdentifierCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{},
|
||||
&ast.BasicScope{},
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"foo ${rand()} success",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
return "42", nil
|
||||
|
@ -54,15 +54,15 @@ func TestIdentifierCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand()}",
|
||||
&Scope{},
|
||||
&ast.BasicScope{},
|
||||
true,
|
||||
},
|
||||
|
||||
{
|
||||
"foo ${rand(42)} ",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
return "42", nil
|
||||
|
@ -75,9 +75,9 @@ func TestIdentifierCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand()} ",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeInt,
|
||||
|
@ -92,9 +92,9 @@ func TestIdentifierCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand(42)} ",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeInt,
|
||||
|
@ -109,9 +109,9 @@ func TestIdentifierCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand(\"foo\", 42)} ",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeString},
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
|
|
|
@ -16,7 +16,7 @@ import (
|
|||
// this structure but we'd rather do that than duplicate the type checking
|
||||
// logic multiple times.
|
||||
type TypeCheck struct {
|
||||
Scope *Scope
|
||||
Scope ast.Scope
|
||||
|
||||
// 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,
|
||||
|
@ -140,7 +140,7 @@ func (v *TypeCheck) visitConcat(n *ast.Concat) {
|
|||
}
|
||||
|
||||
func (v *TypeCheck) visitLiteral(n *ast.LiteralNode) {
|
||||
v.stackPush(n.Type)
|
||||
v.stackPush(n.Typex)
|
||||
}
|
||||
|
||||
func (v *TypeCheck) visitVariableAccess(n *ast.VariableAccess) {
|
||||
|
|
|
@ -9,20 +9,20 @@ import (
|
|||
func TestTypeCheck(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Scope *Scope
|
||||
Scope ast.Scope
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
"foo",
|
||||
&Scope{},
|
||||
&ast.BasicScope{},
|
||||
false,
|
||||
},
|
||||
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: "baz",
|
||||
Type: ast.TypeString,
|
||||
},
|
||||
|
@ -33,9 +33,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand()}",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
return "42", nil
|
||||
|
@ -48,9 +48,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand("42")}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeString},
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
|
@ -64,9 +64,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand(42)}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeString},
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
|
@ -80,9 +80,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand()}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: nil,
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
|
@ -98,9 +98,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand("42")}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: nil,
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
|
@ -116,9 +116,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand("42", 42)}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ArgTypes: nil,
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
|
@ -134,9 +134,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: 42,
|
||||
Type: ast.TypeInt,
|
||||
},
|
||||
|
@ -147,9 +147,9 @@ func TestTypeCheck(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand()}",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeInt,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
return 42, nil
|
||||
|
@ -184,14 +184,14 @@ func TestTypeCheck_implicit(t *testing.T) {
|
|||
|
||||
cases := []struct {
|
||||
Input string
|
||||
Scope *Scope
|
||||
Scope *ast.BasicScope
|
||||
Error bool
|
||||
}{
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: 42,
|
||||
Type: ast.TypeInt,
|
||||
},
|
||||
|
@ -202,9 +202,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${foo(42)}",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"foo": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"foo": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeString},
|
||||
ReturnType: ast.TypeString,
|
||||
},
|
||||
|
@ -215,9 +215,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${foo("42", 42)}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"foo": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"foo": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeString},
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeString,
|
||||
|
@ -237,9 +237,9 @@ func TestTypeCheck_implicit(t *testing.T) {
|
|||
|
||||
// Modify the scope to add our conversion functions.
|
||||
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},
|
||||
ReturnType: ast.TypeString,
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
// prior to running Execute.
|
||||
type Engine struct {
|
||||
// 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
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (e *Engine) scope() *Scope {
|
||||
var scope Scope
|
||||
func (e *Engine) scope() ast.Scope {
|
||||
var scope ast.BasicScope
|
||||
if e.GlobalScope != nil {
|
||||
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
|
||||
// and the identifiers exist.
|
||||
type executeVisitor struct {
|
||||
Scope *Scope
|
||||
Scope ast.Scope
|
||||
|
||||
stack EngineStack
|
||||
err error
|
||||
|
@ -102,7 +102,12 @@ func (v *executeVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) {
|
|||
v.stack.Reset()
|
||||
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 {
|
||||
|
@ -151,7 +156,7 @@ func (v *executeVisitor) visitCall(n *ast.Call) {
|
|||
// Push the result
|
||||
v.stack.Push(&ast.LiteralNode{
|
||||
Value: result,
|
||||
Type: function.ReturnType,
|
||||
Typex: function.ReturnType,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -170,7 +175,7 @@ func (v *executeVisitor) visitConcat(n *ast.Concat) {
|
|||
|
||||
v.stack.Push(&ast.LiteralNode{
|
||||
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{
|
||||
Value: variable.Value,
|
||||
Type: variable.Type,
|
||||
Typex: variable.Type,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -218,33 +223,3 @@ func (s *EngineStack) Pop() *ast.LiteralNode {
|
|||
func (s *EngineStack) Reset() {
|
||||
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) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
Scope *Scope
|
||||
Scope *ast.BasicScope
|
||||
Error bool
|
||||
Result interface{}
|
||||
ResultType ast.Type
|
||||
|
@ -26,9 +26,9 @@ func TestEngineExecute(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: "baz",
|
||||
Type: ast.TypeString,
|
||||
},
|
||||
|
@ -41,9 +41,9 @@ func TestEngineExecute(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${rand()}",
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func([]interface{}) (interface{}, error) {
|
||||
return "42", nil
|
||||
|
@ -58,9 +58,9 @@ func TestEngineExecute(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${rand("foo", "bar")}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"rand": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"rand": ast.Function{
|
||||
ReturnType: ast.TypeString,
|
||||
Variadic: true,
|
||||
VariadicType: ast.TypeString,
|
||||
|
@ -83,9 +83,9 @@ func TestEngineExecute(t *testing.T) {
|
|||
|
||||
{
|
||||
"foo ${bar}",
|
||||
&Scope{
|
||||
VarMap: map[string]Variable{
|
||||
"bar": Variable{
|
||||
&ast.BasicScope{
|
||||
VarMap: map[string]ast.Variable{
|
||||
"bar": ast.Variable{
|
||||
Value: 42,
|
||||
Type: ast.TypeInt,
|
||||
},
|
||||
|
@ -98,9 +98,9 @@ func TestEngineExecute(t *testing.T) {
|
|||
|
||||
{
|
||||
`foo ${foo("42")}`,
|
||||
&Scope{
|
||||
FuncMap: map[string]Function{
|
||||
"foo": Function{
|
||||
&ast.BasicScope{
|
||||
FuncMap: map[string]ast.Function{
|
||||
"foo": ast.Function{
|
||||
ArgTypes: []ast.Type{ast.TypeInt},
|
||||
ReturnType: ast.TypeString,
|
||||
Callback: func(args []interface{}) (interface{}, error) {
|
||||
|
|
|
@ -33,7 +33,7 @@ top:
|
|||
{
|
||||
parserResult = &ast.LiteralNode{
|
||||
Value: "",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
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
|
||||
// has any interpolations).
|
||||
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{
|
||||
Exprs: []ast.Node{$1},
|
||||
Posx: $1.Pos(),
|
||||
|
@ -104,7 +104,7 @@ expr:
|
|||
{
|
||||
$$ = &ast.LiteralNode{
|
||||
Value: $1.Value.(int),
|
||||
Type: ast.TypeInt,
|
||||
Typex: ast.TypeInt,
|
||||
Posx: $1.Pos,
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ expr:
|
|||
{
|
||||
$$ = &ast.LiteralNode{
|
||||
Value: $1.Value.(float64),
|
||||
Type: ast.TypeFloat,
|
||||
Typex: ast.TypeFloat,
|
||||
Posx: $1.Pos,
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ literal:
|
|||
{
|
||||
$$ = &ast.LiteralNode{
|
||||
Value: $1.Value.(string),
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: $1.Pos,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func TestParse(t *testing.T) {
|
|||
false,
|
||||
&ast.LiteralNode{
|
||||
Value: "",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -28,7 +28,7 @@ func TestParse(t *testing.T) {
|
|||
false,
|
||||
&ast.LiteralNode{
|
||||
Value: "foo",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -38,7 +38,7 @@ func TestParse(t *testing.T) {
|
|||
false,
|
||||
&ast.LiteralNode{
|
||||
Value: "${var.foo}",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -51,7 +51,7 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.VariableAccess{
|
||||
|
@ -70,7 +70,7 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.VariableAccess{
|
||||
|
@ -79,7 +79,7 @@ func TestParse(t *testing.T) {
|
|||
},
|
||||
&ast.LiteralNode{
|
||||
Value: " baz",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 15, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -94,12 +94,12 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.LiteralNode{
|
||||
Value: "bar",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 7, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -114,12 +114,12 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.LiteralNode{
|
||||
Value: 42,
|
||||
Type: ast.TypeInt,
|
||||
Typex: ast.TypeInt,
|
||||
Posx: ast.Pos{Column: 7, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -134,12 +134,12 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.LiteralNode{
|
||||
Value: 3.14159,
|
||||
Type: ast.TypeFloat,
|
||||
Typex: ast.TypeFloat,
|
||||
Posx: ast.Pos{Column: 7, Line: 1},
|
||||
},
|
||||
},
|
||||
|
@ -239,7 +239,7 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "foo ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 1, Line: 1},
|
||||
},
|
||||
&ast.Concat{
|
||||
|
@ -247,7 +247,7 @@ func TestParse(t *testing.T) {
|
|||
Exprs: []ast.Node{
|
||||
&ast.LiteralNode{
|
||||
Value: "bar ",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: ast.Pos{Column: 7, Line: 1},
|
||||
},
|
||||
&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{
|
||||
Value: "",
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
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
|
||||
// has any interpolations).
|
||||
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{
|
||||
Exprs: []ast.Node{parserS[parserpt-0].node},
|
||||
Posx: parserS[parserpt-0].node.Pos(),
|
||||
|
@ -417,7 +417,7 @@ parserdefault:
|
|||
{
|
||||
parserVAL.node = &ast.LiteralNode{
|
||||
Value: parserS[parserpt-0].token.Value.(int),
|
||||
Type: ast.TypeInt,
|
||||
Typex: ast.TypeInt,
|
||||
Posx: parserS[parserpt-0].token.Pos,
|
||||
}
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ parserdefault:
|
|||
{
|
||||
parserVAL.node = &ast.LiteralNode{
|
||||
Value: parserS[parserpt-0].token.Value.(float64),
|
||||
Type: ast.TypeFloat,
|
||||
Typex: ast.TypeFloat,
|
||||
Posx: parserS[parserpt-0].token.Pos,
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ parserdefault:
|
|||
{
|
||||
parserVAL.node = &ast.LiteralNode{
|
||||
Value: parserS[parserpt-0].token.Value.(string),
|
||||
Type: ast.TypeString,
|
||||
Typex: ast.TypeString,
|
||||
Posx: parserS[parserpt-0].token.Pos,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue