config/lang: use the new AST stuff

This commit is contained in:
Mitchell Hashimoto 2015-01-14 20:58:46 -08:00
parent c96b3b9ddc
commit 57adfe53f6
12 changed files with 125 additions and 277 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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,

View File

@ -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) {

View File

@ -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,
} }

View File

@ -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
}

View File

@ -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) {

View File

@ -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,
} }
} }

View File

@ -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{

View File

@ -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
}

View File

@ -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 }

View File

@ -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,
} }
} }