From 5f3de02fa9e97940953c1f084d0056166a693a49 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 31 Jan 2016 08:38:37 +0100 Subject: [PATCH] remove config/lang, use hashicorp/hil --- config/config.go | 8 +- config/interpolate.go | 2 +- config/interpolate_funcs.go | 2 +- config/interpolate_funcs_test.go | 8 +- config/interpolate_test.go | 4 +- config/interpolate_walk.go | 6 +- config/interpolate_walk_test.go | 2 +- config/lang.go | 2 +- config/lang/ast/arithmetic.go | 43 -- config/lang/ast/arithmetic_op.go | 13 - config/lang/ast/ast.go | 55 --- config/lang/ast/call.go | 47 -- config/lang/ast/call_test.go | 36 -- config/lang/ast/concat.go | 42 -- config/lang/ast/concat_test.go | 16 - config/lang/ast/literal.go | 33 -- config/lang/ast/literal_test.go | 16 - config/lang/ast/scope.go | 65 --- config/lang/ast/scope_test.go | 39 -- config/lang/ast/stack.go | 25 - config/lang/ast/stack_test.go | 46 -- config/lang/ast/type_string.go | 38 -- config/lang/ast/unary_arithmetic.go | 42 -- config/lang/ast/variable_access.go | 36 -- config/lang/ast/variable_access_test.go | 36 -- config/lang/builtins.go | 186 ------- config/lang/check_identifier.go | 88 ---- config/lang/check_identifier_test.go | 141 ------ config/lang/check_types.go | 369 -------------- config/lang/check_types_test.go | 265 ---------- config/lang/eval.go | 222 --------- config/lang/eval_test.go | 335 ------------- config/lang/lang.y | 173 ------- config/lang/lex.go | 403 --------------- config/lang/lex_test.go | 163 ------ config/lang/parse.go | 32 -- config/lang/parse_test.go | 363 -------------- config/lang/token.go | 1 - config/lang/transform_fixed.go | 26 - config/lang/transform_fixed_test.go | 48 -- config/lang/y.go | 632 ------------------------ config/lang/y.output | 293 ----------- config/raw_config.go | 10 +- config/raw_config_test.go | 2 +- 44 files changed, 23 insertions(+), 4391 deletions(-) delete mode 100644 config/lang/ast/arithmetic.go delete mode 100644 config/lang/ast/arithmetic_op.go delete mode 100644 config/lang/ast/ast.go delete mode 100644 config/lang/ast/call.go delete mode 100644 config/lang/ast/call_test.go delete mode 100644 config/lang/ast/concat.go delete mode 100644 config/lang/ast/concat_test.go delete mode 100644 config/lang/ast/literal.go delete mode 100644 config/lang/ast/literal_test.go delete mode 100644 config/lang/ast/scope.go delete mode 100644 config/lang/ast/scope_test.go delete mode 100644 config/lang/ast/stack.go delete mode 100644 config/lang/ast/stack_test.go delete mode 100644 config/lang/ast/type_string.go delete mode 100644 config/lang/ast/unary_arithmetic.go delete mode 100644 config/lang/ast/variable_access.go delete mode 100644 config/lang/ast/variable_access_test.go delete mode 100644 config/lang/builtins.go delete mode 100644 config/lang/check_identifier.go delete mode 100644 config/lang/check_identifier_test.go delete mode 100644 config/lang/check_types.go delete mode 100644 config/lang/check_types_test.go delete mode 100644 config/lang/eval.go delete mode 100644 config/lang/eval_test.go delete mode 100644 config/lang/lang.y delete mode 100644 config/lang/lex.go delete mode 100644 config/lang/lex_test.go delete mode 100644 config/lang/parse.go delete mode 100644 config/lang/parse_test.go delete mode 100644 config/lang/token.go delete mode 100644 config/lang/transform_fixed.go delete mode 100644 config/lang/transform_fixed_test.go delete mode 100644 config/lang/y.go delete mode 100644 config/lang/y.output diff --git a/config/config.go b/config/config.go index bb3f5f1c8..f42a83ff9 100644 --- a/config/config.go +++ b/config/config.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/hashicorp/go-multierror" - "github.com/hashicorp/terraform/config/lang" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil" + "github.com/hashicorp/hil/ast" "github.com/hashicorp/terraform/flatmap" "github.com/mitchellh/mapstructure" "github.com/mitchellh/reflectwalk" @@ -398,8 +398,8 @@ func (c *Config) Validate() error { r.RawCount.interpolate(func(root ast.Node) (string, error) { // Execute the node but transform the AST so that it returns // a fixed value of "5" for all interpolations. - out, _, err := lang.Eval( - lang.FixedValueTransform( + out, _, err := hil.Eval( + hil.FixedValueTransform( root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}), nil) if err != nil { diff --git a/config/interpolate.go b/config/interpolate.go index 1ccf4b0eb..bfdd114c6 100644 --- a/config/interpolate.go +++ b/config/interpolate.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil/ast" ) // An InterpolatedVariable is a variable reference within an interpolation. diff --git a/config/interpolate_funcs.go b/config/interpolate_funcs.go index f682f46e9..2f73323ca 100644 --- a/config/interpolate_funcs.go +++ b/config/interpolate_funcs.go @@ -16,7 +16,7 @@ import ( "strings" "github.com/apparentlymart/go-cidr/cidr" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil/ast" "github.com/mitchellh/go-homedir" ) diff --git a/config/interpolate_funcs_test.go b/config/interpolate_funcs_test.go index 08db08f44..83ae4a4aa 100644 --- a/config/interpolate_funcs_test.go +++ b/config/interpolate_funcs_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/hashicorp/terraform/config/lang" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil" + "github.com/hashicorp/hil/ast" ) func TestInterpolateFuncCompact(t *testing.T) { @@ -900,12 +900,12 @@ type testFunctionCase struct { func testFunction(t *testing.T, config testFunctionConfig) { for i, tc := range config.Cases { - ast, err := lang.Parse(tc.Input) + ast, err := hil.Parse(tc.Input) if err != nil { t.Fatalf("Case #%d: input: %#v\nerr: %s", i, tc.Input, err) } - out, _, err := lang.Eval(ast, langEvalConfig(config.Vars)) + out, _, err := hil.Eval(ast, langEvalConfig(config.Vars)) if err != nil != tc.Error { t.Fatalf("Case #%d:\ninput: %#v\nerr: %s", i, tc.Input, err) } diff --git a/config/interpolate_test.go b/config/interpolate_test.go index 3328571cc..9842251e3 100644 --- a/config/interpolate_test.go +++ b/config/interpolate_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - "github.com/hashicorp/terraform/config/lang" + "github.com/hashicorp/hil" ) func TestNewInterpolatedVariable(t *testing.T) { @@ -222,7 +222,7 @@ func TestDetectVariables(t *testing.T) { } for _, tc := range cases { - ast, err := lang.Parse(tc.Input) + ast, err := hil.Parse(tc.Input) if err != nil { t.Fatalf("%s\n\nInput: %s", err, tc.Input) } diff --git a/config/interpolate_walk.go b/config/interpolate_walk.go index 075355831..333cf33ed 100644 --- a/config/interpolate_walk.go +++ b/config/interpolate_walk.go @@ -5,8 +5,8 @@ import ( "reflect" "strings" - "github.com/hashicorp/terraform/config/lang" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil" + "github.com/hashicorp/hil/ast" "github.com/mitchellh/reflectwalk" ) @@ -113,7 +113,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error { return nil } - astRoot, err := lang.Parse(v.String()) + astRoot, err := hil.Parse(v.String()) if err != nil { return err } diff --git a/config/interpolate_walk_test.go b/config/interpolate_walk_test.go index b5c9a3a92..b7c308cd4 100644 --- a/config/interpolate_walk_test.go +++ b/config/interpolate_walk_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil/ast" "github.com/mitchellh/reflectwalk" ) diff --git a/config/lang.go b/config/lang.go index 7b960ead3..890d30beb 100644 --- a/config/lang.go +++ b/config/lang.go @@ -1,7 +1,7 @@ package config import ( - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil/ast" ) type noopNode struct{} diff --git a/config/lang/ast/arithmetic.go b/config/lang/ast/arithmetic.go deleted file mode 100644 index 94dc24f89..000000000 --- a/config/lang/ast/arithmetic.go +++ /dev/null @@ -1,43 +0,0 @@ -package ast - -import ( - "bytes" - "fmt" -) - -// Arithmetic represents a node where the result is arithmetic of -// two or more operands in the order given. -type Arithmetic struct { - Op ArithmeticOp - Exprs []Node - Posx Pos -} - -func (n *Arithmetic) Accept(v Visitor) Node { - for i, expr := range n.Exprs { - n.Exprs[i] = expr.Accept(v) - } - - return v(n) -} - -func (n *Arithmetic) Pos() Pos { - return n.Posx -} - -func (n *Arithmetic) GoString() string { - return fmt.Sprintf("*%#v", *n) -} - -func (n *Arithmetic) String() string { - var b bytes.Buffer - for _, expr := range n.Exprs { - b.WriteString(fmt.Sprintf("%s", expr)) - } - - return b.String() -} - -func (n *Arithmetic) Type(Scope) (Type, error) { - return TypeInt, nil -} diff --git a/config/lang/ast/arithmetic_op.go b/config/lang/ast/arithmetic_op.go deleted file mode 100644 index e36dd42dc..000000000 --- a/config/lang/ast/arithmetic_op.go +++ /dev/null @@ -1,13 +0,0 @@ -package ast - -// ArithmeticOp is the operation to use for the math. -type ArithmeticOp int - -const ( - ArithmeticOpInvalid ArithmeticOp = 0 - ArithmeticOpAdd ArithmeticOp = iota - ArithmeticOpSub - ArithmeticOpMul - ArithmeticOpDiv - ArithmeticOpMod -) diff --git a/config/lang/ast/ast.go b/config/lang/ast/ast.go deleted file mode 100644 index 1d784c78a..000000000 --- a/config/lang/ast/ast.go +++ /dev/null @@ -1,55 +0,0 @@ -package ast - -import ( - "fmt" -) - -// Node is the interface that all AST nodes must implement. -type Node interface { - // Accept is called to dispatch to the visitors. It must return the - // resulting Node (which might be different in an AST transform). - Accept(Visitor) Node - - // Pos returns the position of this node in some source. - Pos() Pos - - // Type returns the type of this node for the given context. - Type(Scope) (Type, error) -} - -// Pos is the starting position of an AST node -type Pos struct { - Column, Line int // Column/Line number, starting at 1 -} - -func (p Pos) String() string { - return fmt.Sprintf("%d:%d", p.Line, p.Column) -} - -// Visitors are just implementations of this function. -// -// The function must return the Node to replace this node with. "nil" is -// _not_ a valid return value. If there is no replacement, the original node -// should be returned. We build this replacement directly into the visitor -// pattern since AST transformations are a common and useful tool and -// building it into the AST itself makes it required for future Node -// implementations and very easy to do. -// -// Note that this isn't a true implementation of the visitor pattern, which -// generally requires proper type dispatch on the function. However, -// implementing this basic visitor pattern style is still very useful even -// if you have to type switch. -type Visitor func(Node) Node - -//go:generate stringer -type=Type - -// Type is the type of any value. -type Type uint32 - -const ( - TypeInvalid Type = 0 - TypeAny Type = 1 << iota - TypeString - TypeInt - TypeFloat -) diff --git a/config/lang/ast/call.go b/config/lang/ast/call.go deleted file mode 100644 index 055701102..000000000 --- a/config/lang/ast/call.go +++ /dev/null @@ -1,47 +0,0 @@ -package ast - -import ( - "fmt" - "strings" -) - -// Call represents a function call. -type Call struct { - Func string - Args []Node - Posx Pos -} - -func (n *Call) Accept(v Visitor) Node { - for i, a := range n.Args { - n.Args[i] = a.Accept(v) - } - - return v(n) -} - -func (n *Call) Pos() Pos { - return n.Posx -} - -func (n *Call) String() string { - args := make([]string, len(n.Args)) - for i, arg := range n.Args { - args[i] = fmt.Sprintf("%s", arg) - } - - return fmt.Sprintf("Call(%s, %s)", n.Func, strings.Join(args, ", ")) -} - -func (n *Call) Type(s Scope) (Type, error) { - f, ok := s.LookupFunc(n.Func) - if !ok { - return TypeInvalid, fmt.Errorf("unknown function: %s", n.Func) - } - - return f.ReturnType, nil -} - -func (n *Call) GoString() string { - return fmt.Sprintf("*%#v", *n) -} diff --git a/config/lang/ast/call_test.go b/config/lang/ast/call_test.go deleted file mode 100644 index ef63888d2..000000000 --- a/config/lang/ast/call_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "testing" -) - -func TestCallType(t *testing.T) { - c := &Call{Func: "foo"} - scope := &BasicScope{ - FuncMap: map[string]Function{ - "foo": Function{ReturnType: TypeString}, - }, - } - - actual, err := c.Type(scope) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != TypeString { - t.Fatalf("bad: %s", actual) - } -} - -func TestCallType_invalid(t *testing.T) { - c := &Call{Func: "bar"} - scope := &BasicScope{ - FuncMap: map[string]Function{ - "foo": Function{ReturnType: TypeString}, - }, - } - - _, err := c.Type(scope) - if err == nil { - t.Fatal("should error") - } -} diff --git a/config/lang/ast/concat.go b/config/lang/ast/concat.go deleted file mode 100644 index 0246a3bc1..000000000 --- a/config/lang/ast/concat.go +++ /dev/null @@ -1,42 +0,0 @@ -package ast - -import ( - "bytes" - "fmt" -) - -// Concat represents a node where the result of two or more expressions are -// concatenated. The result of all expressions must be a string. -type Concat struct { - Exprs []Node - Posx Pos -} - -func (n *Concat) Accept(v Visitor) Node { - for i, expr := range n.Exprs { - n.Exprs[i] = expr.Accept(v) - } - - return v(n) -} - -func (n *Concat) Pos() Pos { - return n.Posx -} - -func (n *Concat) GoString() string { - return fmt.Sprintf("*%#v", *n) -} - -func (n *Concat) String() string { - var b bytes.Buffer - for _, expr := range n.Exprs { - b.WriteString(fmt.Sprintf("%s", expr)) - } - - return b.String() -} - -func (n *Concat) Type(Scope) (Type, error) { - return TypeString, nil -} diff --git a/config/lang/ast/concat_test.go b/config/lang/ast/concat_test.go deleted file mode 100644 index 65fa67601..000000000 --- a/config/lang/ast/concat_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package ast - -import ( - "testing" -) - -func TestConcatType(t *testing.T) { - c := &Concat{} - actual, err := c.Type(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != TypeString { - t.Fatalf("bad: %s", actual) - } -} diff --git a/config/lang/ast/literal.go b/config/lang/ast/literal.go deleted file mode 100644 index 1714ff026..000000000 --- a/config/lang/ast/literal.go +++ /dev/null @@ -1,33 +0,0 @@ -package ast - -import ( - "fmt" -) - -// LiteralNode represents a single literal value, such as "foo" or -// 42 or 3.14159. Based on the Type, the Value can be safely cast. -type LiteralNode struct { - Value interface{} - Typex Type - Posx Pos -} - -func (n *LiteralNode) Accept(v Visitor) Node { - return v(n) -} - -func (n *LiteralNode) Pos() Pos { - return n.Posx -} - -func (n *LiteralNode) GoString() string { - return fmt.Sprintf("*%#v", *n) -} - -func (n *LiteralNode) String() string { - return fmt.Sprintf("Literal(%s, %v)", n.Typex, n.Value) -} - -func (n *LiteralNode) Type(Scope) (Type, error) { - return n.Typex, nil -} diff --git a/config/lang/ast/literal_test.go b/config/lang/ast/literal_test.go deleted file mode 100644 index 2759d7722..000000000 --- a/config/lang/ast/literal_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package ast - -import ( - "testing" -) - -func TestLiteralNodeType(t *testing.T) { - c := &LiteralNode{Typex: TypeString} - actual, err := c.Type(nil) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != TypeString { - t.Fatalf("bad: %s", actual) - } -} diff --git a/config/lang/ast/scope.go b/config/lang/ast/scope.go deleted file mode 100644 index 77c3ee79a..000000000 --- a/config/lang/ast/scope.go +++ /dev/null @@ -1,65 +0,0 @@ -package ast - -// Scope is the interface used to look up variables and functions while -// evaluating. How these functions/variables are defined are up to the caller. -type Scope interface { - LookupFunc(string) (Function, bool) - LookupVar(string) (Variable, bool) -} - -// Variable is a variable value for execution given as input to the engine. -// It records the value of a variables along with their type. -type Variable struct { - Value interface{} - Type Type -} - -// Function defines a function that can be executed by the engine. -// 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 []Type - ReturnType Type - - // 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 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) -} - -// BasicScope is a simple scope that looks up variables and functions -// using a map. -type BasicScope struct { - FuncMap map[string]Function - VarMap map[string]Variable -} - -func (s *BasicScope) LookupFunc(n string) (Function, bool) { - if s == nil { - return Function{}, false - } - - v, ok := s.FuncMap[n] - return v, ok -} - -func (s *BasicScope) LookupVar(n string) (Variable, bool) { - if s == nil { - return Variable{}, false - } - - v, ok := s.VarMap[n] - return v, ok -} diff --git a/config/lang/ast/scope_test.go b/config/lang/ast/scope_test.go deleted file mode 100644 index b1484a1fd..000000000 --- a/config/lang/ast/scope_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package ast - -import ( - "testing" -) - -func TestBasicScope_impl(t *testing.T) { - var _ Scope = new(BasicScope) -} - -func TestBasicScopeLookupFunc(t *testing.T) { - scope := &BasicScope{ - FuncMap: map[string]Function{ - "foo": Function{}, - }, - } - - if _, ok := scope.LookupFunc("bar"); ok { - t.Fatal("should not find bar") - } - if _, ok := scope.LookupFunc("foo"); !ok { - t.Fatal("should find foo") - } -} - -func TestBasicScopeLookupVar(t *testing.T) { - scope := &BasicScope{ - VarMap: map[string]Variable{ - "foo": Variable{}, - }, - } - - if _, ok := scope.LookupVar("bar"); ok { - t.Fatal("should not find bar") - } - if _, ok := scope.LookupVar("foo"); !ok { - t.Fatal("should find foo") - } -} diff --git a/config/lang/ast/stack.go b/config/lang/ast/stack.go deleted file mode 100644 index bd2bc1578..000000000 --- a/config/lang/ast/stack.go +++ /dev/null @@ -1,25 +0,0 @@ -package ast - -// Stack is a stack of Node. -type Stack struct { - stack []Node -} - -func (s *Stack) Len() int { - return len(s.stack) -} - -func (s *Stack) Push(n Node) { - s.stack = append(s.stack, n) -} - -func (s *Stack) Pop() Node { - x := s.stack[len(s.stack)-1] - s.stack[len(s.stack)-1] = nil - s.stack = s.stack[:len(s.stack)-1] - return x -} - -func (s *Stack) Reset() { - s.stack = nil -} diff --git a/config/lang/ast/stack_test.go b/config/lang/ast/stack_test.go deleted file mode 100644 index 95a9d9255..000000000 --- a/config/lang/ast/stack_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package ast - -import ( - "reflect" - "testing" -) - -func TestStack(t *testing.T) { - var s Stack - if s.Len() != 0 { - t.Fatalf("bad: %d", s.Len()) - } - - n := &LiteralNode{Value: 42} - s.Push(n) - - if s.Len() != 1 { - t.Fatalf("bad: %d", s.Len()) - } - - actual := s.Pop() - if !reflect.DeepEqual(actual, n) { - t.Fatalf("bad: %#v", actual) - } - - if s.Len() != 0 { - t.Fatalf("bad: %d", s.Len()) - } -} - -func TestStack_reset(t *testing.T) { - var s Stack - - n := &LiteralNode{Value: 42} - s.Push(n) - - if s.Len() != 1 { - t.Fatalf("bad: %d", s.Len()) - } - - s.Reset() - - if s.Len() != 0 { - t.Fatalf("bad: %d", s.Len()) - } -} diff --git a/config/lang/ast/type_string.go b/config/lang/ast/type_string.go deleted file mode 100644 index 5410e011e..000000000 --- a/config/lang/ast/type_string.go +++ /dev/null @@ -1,38 +0,0 @@ -// Code generated by "stringer -type=Type"; DO NOT EDIT - -package ast - -import "fmt" - -const ( - _Type_name_0 = "TypeInvalid" - _Type_name_1 = "TypeAny" - _Type_name_2 = "TypeString" - _Type_name_3 = "TypeInt" - _Type_name_4 = "TypeFloat" -) - -var ( - _Type_index_0 = [...]uint8{0, 11} - _Type_index_1 = [...]uint8{0, 7} - _Type_index_2 = [...]uint8{0, 10} - _Type_index_3 = [...]uint8{0, 7} - _Type_index_4 = [...]uint8{0, 9} -) - -func (i Type) String() string { - switch { - case i == 0: - return _Type_name_0 - case i == 2: - return _Type_name_1 - case i == 4: - return _Type_name_2 - case i == 8: - return _Type_name_3 - case i == 16: - return _Type_name_4 - default: - return fmt.Sprintf("Type(%d)", i) - } -} diff --git a/config/lang/ast/unary_arithmetic.go b/config/lang/ast/unary_arithmetic.go deleted file mode 100644 index d6b65b365..000000000 --- a/config/lang/ast/unary_arithmetic.go +++ /dev/null @@ -1,42 +0,0 @@ -package ast - -import ( - "fmt" -) - -// UnaryArithmetic represents a node where the result is arithmetic of -// one operands -type UnaryArithmetic struct { - Op ArithmeticOp - Expr Node - Posx Pos -} - -func (n *UnaryArithmetic) Accept(v Visitor) Node { - n.Expr = n.Expr.Accept(v) - - return v(n) -} - -func (n *UnaryArithmetic) Pos() Pos { - return n.Posx -} - -func (n *UnaryArithmetic) GoString() string { - return fmt.Sprintf("*%#v", *n) -} - -func (n *UnaryArithmetic) String() string { - var sign rune - switch n.Op { - case ArithmeticOpAdd: - sign = '+' - case ArithmeticOpSub: - sign = '-' - } - return fmt.Sprintf("%c%s", sign, n.Expr) -} - -func (n *UnaryArithmetic) Type(Scope) (Type, error) { - return TypeInt, nil -} diff --git a/config/lang/ast/variable_access.go b/config/lang/ast/variable_access.go deleted file mode 100644 index 4c1362d75..000000000 --- a/config/lang/ast/variable_access.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "fmt" -) - -// VariableAccess represents a variable access. -type VariableAccess struct { - Name string - Posx Pos -} - -func (n *VariableAccess) Accept(v Visitor) Node { - return v(n) -} - -func (n *VariableAccess) Pos() Pos { - return n.Posx -} - -func (n *VariableAccess) GoString() string { - return fmt.Sprintf("*%#v", *n) -} - -func (n *VariableAccess) String() string { - return fmt.Sprintf("Variable(%s)", n.Name) -} - -func (n *VariableAccess) Type(s Scope) (Type, error) { - v, ok := s.LookupVar(n.Name) - if !ok { - return TypeInvalid, fmt.Errorf("unknown variable: %s", n.Name) - } - - return v.Type, nil -} diff --git a/config/lang/ast/variable_access_test.go b/config/lang/ast/variable_access_test.go deleted file mode 100644 index 1880bc514..000000000 --- a/config/lang/ast/variable_access_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package ast - -import ( - "testing" -) - -func TestVariableAccessType(t *testing.T) { - c := &VariableAccess{Name: "foo"} - scope := &BasicScope{ - VarMap: map[string]Variable{ - "foo": Variable{Type: TypeString}, - }, - } - - actual, err := c.Type(scope) - if err != nil { - t.Fatalf("err: %s", err) - } - if actual != TypeString { - t.Fatalf("bad: %s", actual) - } -} - -func TestVariableAccessType_invalid(t *testing.T) { - c := &VariableAccess{Name: "bar"} - scope := &BasicScope{ - VarMap: map[string]Variable{ - "foo": Variable{Type: TypeString}, - }, - } - - _, err := c.Type(scope) - if err == nil { - t.Fatal("should error") - } -} diff --git a/config/lang/builtins.go b/config/lang/builtins.go deleted file mode 100644 index 457a5ef37..000000000 --- a/config/lang/builtins.go +++ /dev/null @@ -1,186 +0,0 @@ -package lang - -import ( - "strconv" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -// NOTE: All builtins are tested in engine_test.go - -func registerBuiltins(scope *ast.BasicScope) *ast.BasicScope { - if scope == nil { - scope = new(ast.BasicScope) - } - if scope.FuncMap == nil { - scope.FuncMap = make(map[string]ast.Function) - } - - // Implicit conversions - scope.FuncMap["__builtin_FloatToInt"] = builtinFloatToInt() - scope.FuncMap["__builtin_FloatToString"] = builtinFloatToString() - scope.FuncMap["__builtin_IntToFloat"] = builtinIntToFloat() - scope.FuncMap["__builtin_IntToString"] = builtinIntToString() - scope.FuncMap["__builtin_StringToInt"] = builtinStringToInt() - - // Math operations - scope.FuncMap["__builtin_UnaryIntMath"] = builtinUnaryIntMath() - scope.FuncMap["__builtin_UnaryFloatMath"] = builtinUnaryFloatMath() - scope.FuncMap["__builtin_IntMath"] = builtinIntMath() - scope.FuncMap["__builtin_FloatMath"] = builtinFloatMath() - return scope -} - -func builtinUnaryIntMath() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - Variadic: false, - ReturnType: ast.TypeInt, - Callback: func(args []interface{}) (interface{}, error) { - op := args[0].(ast.ArithmeticOp) - result := args[1].(int) - switch op { - case ast.ArithmeticOpAdd: - result = result - case ast.ArithmeticOpSub: - result = -result - } - - return result, nil - }, - } -} - -func builtinUnaryFloatMath() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeFloat}, - Variadic: false, - ReturnType: ast.TypeFloat, - Callback: func(args []interface{}) (interface{}, error) { - op := args[0].(ast.ArithmeticOp) - result := args[1].(float64) - switch op { - case ast.ArithmeticOpAdd: - result = result - case ast.ArithmeticOpSub: - result = -result - } - - return result, nil - }, - } -} - -func builtinFloatMath() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - Variadic: true, - VariadicType: ast.TypeFloat, - ReturnType: ast.TypeFloat, - Callback: func(args []interface{}) (interface{}, error) { - op := args[0].(ast.ArithmeticOp) - result := args[1].(float64) - for _, raw := range args[2:] { - arg := raw.(float64) - switch op { - case ast.ArithmeticOpAdd: - result += arg - case ast.ArithmeticOpSub: - result -= arg - case ast.ArithmeticOpMul: - result *= arg - case ast.ArithmeticOpDiv: - result /= arg - } - } - - return result, nil - }, - } -} - -func builtinIntMath() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - Variadic: true, - VariadicType: ast.TypeInt, - ReturnType: ast.TypeInt, - Callback: func(args []interface{}) (interface{}, error) { - op := args[0].(ast.ArithmeticOp) - result := args[1].(int) - for _, raw := range args[2:] { - arg := raw.(int) - switch op { - case ast.ArithmeticOpAdd: - result += arg - case ast.ArithmeticOpSub: - result -= arg - case ast.ArithmeticOpMul: - result *= arg - case ast.ArithmeticOpDiv: - result /= arg - case ast.ArithmeticOpMod: - result = result % arg - } - } - - return result, nil - }, - } -} - -func builtinFloatToInt() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeFloat}, - ReturnType: ast.TypeInt, - Callback: func(args []interface{}) (interface{}, error) { - return int(args[0].(float64)), nil - }, - } -} - -func builtinFloatToString() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeFloat}, - ReturnType: ast.TypeString, - Callback: func(args []interface{}) (interface{}, error) { - return strconv.FormatFloat( - args[0].(float64), 'g', -1, 64), nil - }, - } -} - -func builtinIntToFloat() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - ReturnType: ast.TypeFloat, - Callback: func(args []interface{}) (interface{}, error) { - return float64(args[0].(int)), nil - }, - } -} - -func builtinIntToString() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - ReturnType: ast.TypeString, - Callback: func(args []interface{}) (interface{}, error) { - return strconv.FormatInt(int64(args[0].(int)), 10), nil - }, - } -} - -func builtinStringToInt() ast.Function { - return ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - ReturnType: ast.TypeString, - Callback: func(args []interface{}) (interface{}, error) { - v, err := strconv.ParseInt(args[0].(string), 0, 0) - if err != nil { - return nil, err - } - - return int(v), nil - }, - } -} diff --git a/config/lang/check_identifier.go b/config/lang/check_identifier.go deleted file mode 100644 index b835e8e4e..000000000 --- a/config/lang/check_identifier.go +++ /dev/null @@ -1,88 +0,0 @@ -package lang - -import ( - "fmt" - "sync" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -// IdentifierCheck is a SemanticCheck that checks that all identifiers -// resolve properly and that the right number of arguments are passed -// to functions. -type IdentifierCheck struct { - Scope ast.Scope - - err error - lock sync.Mutex -} - -func (c *IdentifierCheck) Visit(root ast.Node) error { - c.lock.Lock() - defer c.lock.Unlock() - defer c.reset() - root.Accept(c.visit) - return c.err -} - -func (c *IdentifierCheck) visit(raw ast.Node) ast.Node { - if c.err != nil { - return raw - } - - switch n := raw.(type) { - case *ast.Call: - c.visitCall(n) - case *ast.VariableAccess: - c.visitVariableAccess(n) - case *ast.Concat: - // Ignore - case *ast.LiteralNode: - // Ignore - default: - // Ignore - } - - // We never do replacement with this visitor - return raw -} - -func (c *IdentifierCheck) visitCall(n *ast.Call) { - // Look up the function in the map - function, ok := c.Scope.LookupFunc(n.Func) - if !ok { - c.createErr(n, fmt.Sprintf("unknown function called: %s", n.Func)) - 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(args) != len(function.ArgTypes) { - c.createErr(n, fmt.Sprintf( - "%s: expected %d arguments, got %d", - n.Func, len(function.ArgTypes), len(n.Args))) - return - } -} - -func (c *IdentifierCheck) visitVariableAccess(n *ast.VariableAccess) { - // Look up the variable in the map - if _, ok := c.Scope.LookupVar(n.Name); !ok { - c.createErr(n, fmt.Sprintf( - "unknown variable accessed: %s", n.Name)) - return - } -} - -func (c *IdentifierCheck) createErr(n ast.Node, str string) { - c.err = fmt.Errorf("%s: %s", n.Pos(), str) -} - -func (c *IdentifierCheck) reset() { - c.err = nil -} diff --git a/config/lang/check_identifier_test.go b/config/lang/check_identifier_test.go deleted file mode 100644 index fe76be1d4..000000000 --- a/config/lang/check_identifier_test.go +++ /dev/null @@ -1,141 +0,0 @@ -package lang - -import ( - "testing" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -func TestIdentifierCheck(t *testing.T) { - cases := []struct { - Input string - Scope ast.Scope - Error bool - }{ - { - "foo", - &ast.BasicScope{}, - false, - }, - - { - "foo ${bar} success", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: "baz", - Type: ast.TypeString, - }, - }, - }, - false, - }, - - { - "foo ${bar}", - &ast.BasicScope{}, - true, - }, - - { - "foo ${rand()} success", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - "foo ${rand()}", - &ast.BasicScope{}, - true, - }, - - { - "foo ${rand(42)} ", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - true, - }, - - { - "foo ${rand()} ", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Variadic: true, - VariadicType: ast.TypeInt, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - "foo ${rand(42)} ", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Variadic: true, - VariadicType: ast.TypeInt, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - "foo ${rand(\"foo\", 42)} ", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.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 { - node, err := Parse(tc.Input) - if err != nil { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - - visitor := &IdentifierCheck{Scope: tc.Scope} - err = visitor.Visit(node) - if err != nil != tc.Error { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - } -} diff --git a/config/lang/check_types.go b/config/lang/check_types.go deleted file mode 100644 index 0ff6ac93b..000000000 --- a/config/lang/check_types.go +++ /dev/null @@ -1,369 +0,0 @@ -package lang - -import ( - "fmt" - "sync" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -// TypeCheck implements ast.Visitor for type checking an AST tree. -// It requires some configuration to look up the type of nodes. -// -// It also optionally will not type error and will insert an implicit -// type conversions for specific types if specified by the Implicit -// field. Note that this is kind of organizationally weird to put into -// this structure but we'd rather do that than duplicate the type checking -// logic multiple times. -type TypeCheck struct { - 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, - // the key of the second map is the to type, and the final string - // value is the function to call (which must be registered in the Scope). - Implicit map[ast.Type]map[ast.Type]string - - // Stack of types. This shouldn't be used directly except by implementations - // of TypeCheckNode. - Stack []ast.Type - - err error - lock sync.Mutex -} - -// TypeCheckNode is the interface that must be implemented by any -// ast.Node that wants to support type-checking. If the type checker -// encounters a node that doesn't implement this, it will error. -type TypeCheckNode interface { - TypeCheck(*TypeCheck) (ast.Node, error) -} - -func (v *TypeCheck) Visit(root ast.Node) error { - v.lock.Lock() - defer v.lock.Unlock() - defer v.reset() - root.Accept(v.visit) - return v.err -} - -func (v *TypeCheck) visit(raw ast.Node) ast.Node { - if v.err != nil { - return raw - } - - var result ast.Node - var err error - switch n := raw.(type) { - case *ast.UnaryArithmetic: - tc := &typeCheckUnaryArithmetic{n} - result, err = tc.TypeCheck(v) - case *ast.Arithmetic: - tc := &typeCheckArithmetic{n} - result, err = tc.TypeCheck(v) - case *ast.Call: - tc := &typeCheckCall{n} - result, err = tc.TypeCheck(v) - case *ast.Concat: - tc := &typeCheckConcat{n} - result, err = tc.TypeCheck(v) - case *ast.LiteralNode: - tc := &typeCheckLiteral{n} - result, err = tc.TypeCheck(v) - case *ast.VariableAccess: - tc := &typeCheckVariableAccess{n} - result, err = tc.TypeCheck(v) - default: - tc, ok := raw.(TypeCheckNode) - if !ok { - err = fmt.Errorf("unknown node for type check: %#v", raw) - break - } - - result, err = tc.TypeCheck(v) - } - - if err != nil { - pos := raw.Pos() - v.err = fmt.Errorf("At column %d, line %d: %s", - pos.Column, pos.Line, err) - } - - return result -} - -type typeCheckUnaryArithmetic struct { - n *ast.UnaryArithmetic -} - -func (tc *typeCheckUnaryArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { - // Only support + or - as unary op - if tc.n.Op != ast.ArithmeticOpAdd && tc.n.Op != ast.ArithmeticOpSub { - fmt.Printf("%+v\n", tc.n.Op) - return nil, fmt.Errorf("only + or - supported as unary operator") - } - expr := v.StackPop() - - mathFunc := "__builtin_UnaryIntMath" - mathType := ast.TypeInt - switch expr { - case ast.TypeInt: - mathFunc = "__builtin_UnaryIntMath" - mathType = expr - case ast.TypeFloat: - mathFunc = "__builtin_UnaryFloatMath" - mathType = expr - } - - // Return type - v.StackPush(mathType) - - args := make([]ast.Node, 2) - args[0] = &ast.LiteralNode{ - Value: tc.n.Op, - Typex: ast.TypeInt, - Posx: tc.n.Pos(), - } - args[1] = tc.n.Expr - // Replace our node with a call to the proper function. This isn't - // type checked but we already verified types. - return &ast.Call{ - Func: mathFunc, - Args: args, - Posx: tc.n.Pos(), - }, nil -} - -type typeCheckArithmetic struct { - n *ast.Arithmetic -} - -func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) { - // The arguments are on the stack in reverse order, so pop them off. - exprs := make([]ast.Type, len(tc.n.Exprs)) - for i, _ := range tc.n.Exprs { - exprs[len(tc.n.Exprs)-1-i] = v.StackPop() - } - - // Determine the resulting type we want. We do this by going over - // every expression until we find one with a type we recognize. - // We do this because the first expr might be a string ("var.foo") - // and we need to know what to implicit to. - mathFunc := "__builtin_IntMath" - mathType := ast.TypeInt - for _, v := range exprs { - exit := true - switch v { - case ast.TypeInt: - mathFunc = "__builtin_IntMath" - mathType = v - case ast.TypeFloat: - mathFunc = "__builtin_FloatMath" - mathType = v - default: - exit = false - } - - // We found the type, so leave - if exit { - break - } - } - - // Verify the args - for i, arg := range exprs { - if arg != mathType { - cn := v.ImplicitConversion(exprs[i], mathType, tc.n.Exprs[i]) - if cn != nil { - tc.n.Exprs[i] = cn - continue - } - - return nil, fmt.Errorf( - "operand %d should be %s, got %s", - i+1, mathType, arg) - } - } - - // Modulo doesn't work for floats - if mathType == ast.TypeFloat && tc.n.Op == ast.ArithmeticOpMod { - return nil, fmt.Errorf("modulo cannot be used with floats") - } - - // Return type - v.StackPush(mathType) - - // Replace our node with a call to the proper function. This isn't - // type checked but we already verified types. - args := make([]ast.Node, len(tc.n.Exprs)+1) - args[0] = &ast.LiteralNode{ - Value: tc.n.Op, - Typex: ast.TypeInt, - Posx: tc.n.Pos(), - } - copy(args[1:], tc.n.Exprs) - return &ast.Call{ - Func: mathFunc, - Args: args, - Posx: tc.n.Pos(), - }, nil -} - -type typeCheckCall struct { - n *ast.Call -} - -func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { - // Look up the function in the map - function, ok := v.Scope.LookupFunc(tc.n.Func) - if !ok { - return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) - } - - // The arguments are on the stack in reverse order, so pop them off. - args := make([]ast.Type, len(tc.n.Args)) - for i, _ := range tc.n.Args { - args[len(tc.n.Args)-1-i] = v.StackPop() - } - - // Verify the args - for i, expected := range function.ArgTypes { - if expected == ast.TypeAny { - continue - } - - if args[i] != expected { - cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) - if cn != nil { - tc.n.Args[i] = cn - continue - } - - return nil, fmt.Errorf( - "%s: argument %d should be %s, got %s", - tc.n.Func, i+1, expected, args[i]) - } - } - - // If we're variadic, then verify the types there - if function.Variadic && function.VariadicType != ast.TypeAny { - args = args[len(function.ArgTypes):] - for i, t := range args { - if t != function.VariadicType { - realI := i + len(function.ArgTypes) - cn := v.ImplicitConversion( - t, function.VariadicType, tc.n.Args[realI]) - if cn != nil { - tc.n.Args[realI] = cn - continue - } - - return nil, fmt.Errorf( - "%s: argument %d should be %s, got %s", - tc.n.Func, realI, - function.VariadicType, t) - } - } - } - - // Return type - v.StackPush(function.ReturnType) - - return tc.n, nil -} - -type typeCheckConcat struct { - n *ast.Concat -} - -func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) { - n := tc.n - types := make([]ast.Type, len(n.Exprs)) - for i, _ := range n.Exprs { - types[len(n.Exprs)-1-i] = v.StackPop() - } - - // All concat args must be strings, so validate that - for i, t := range types { - if t != ast.TypeString { - cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) - if cn != nil { - n.Exprs[i] = cn - continue - } - - return nil, fmt.Errorf( - "argument %d must be a string", i+1) - } - } - - // This always results in type string - v.StackPush(ast.TypeString) - - return n, nil -} - -type typeCheckLiteral struct { - n *ast.LiteralNode -} - -func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { - v.StackPush(tc.n.Typex) - return tc.n, nil -} - -type typeCheckVariableAccess struct { - n *ast.VariableAccess -} - -func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { - // Look up the variable in the map - variable, ok := v.Scope.LookupVar(tc.n.Name) - if !ok { - return nil, fmt.Errorf( - "unknown variable accessed: %s", tc.n.Name) - } - - // Add the type to the stack - v.StackPush(variable.Type) - - return tc.n, nil -} - -func (v *TypeCheck) ImplicitConversion( - actual ast.Type, expected ast.Type, n ast.Node) ast.Node { - if v.Implicit == nil { - return nil - } - - fromMap, ok := v.Implicit[actual] - if !ok { - return nil - } - - toFunc, ok := fromMap[expected] - if !ok { - return nil - } - - return &ast.Call{ - Func: toFunc, - Args: []ast.Node{n}, - Posx: n.Pos(), - } -} - -func (v *TypeCheck) reset() { - v.Stack = nil - v.err = nil -} - -func (v *TypeCheck) StackPush(t ast.Type) { - v.Stack = append(v.Stack, t) -} - -func (v *TypeCheck) StackPop() ast.Type { - var x ast.Type - x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] - return x -} diff --git a/config/lang/check_types_test.go b/config/lang/check_types_test.go deleted file mode 100644 index 6087f98d5..000000000 --- a/config/lang/check_types_test.go +++ /dev/null @@ -1,265 +0,0 @@ -package lang - -import ( - "testing" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -func TestTypeCheck(t *testing.T) { - cases := []struct { - Input string - Scope ast.Scope - Error bool - }{ - { - "foo", - &ast.BasicScope{}, - false, - }, - - { - "foo ${bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: "baz", - Type: ast.TypeString, - }, - }, - }, - false, - }, - - { - "foo ${rand()}", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - `foo ${rand("42")}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ArgTypes: []ast.Type{ast.TypeString}, - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - `foo ${rand(42)}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ArgTypes: []ast.Type{ast.TypeString}, - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - true, - }, - - { - `foo ${rand()}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ArgTypes: nil, - ReturnType: ast.TypeString, - Variadic: true, - VariadicType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - `foo ${rand("42")}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ArgTypes: nil, - ReturnType: ast.TypeString, - Variadic: true, - VariadicType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - }, - - { - `foo ${rand("42", 42)}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ArgTypes: nil, - ReturnType: ast.TypeString, - Variadic: true, - VariadicType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - true, - }, - - { - "foo ${bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 42, - Type: ast.TypeInt, - }, - }, - }, - true, - }, - - { - "foo ${rand()}", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeInt, - Callback: func([]interface{}) (interface{}, error) { - return 42, nil - }, - }, - }, - }, - true, - }, - } - - for _, tc := range cases { - node, err := Parse(tc.Input) - if err != nil { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - - visitor := &TypeCheck{Scope: tc.Scope} - err = visitor.Visit(node) - if err != nil != tc.Error { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - } -} - -func TestTypeCheck_implicit(t *testing.T) { - implicitMap := map[ast.Type]map[ast.Type]string{ - ast.TypeInt: { - ast.TypeString: "intToString", - }, - } - - cases := []struct { - Input string - Scope *ast.BasicScope - Error bool - }{ - { - "foo ${bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 42, - Type: ast.TypeInt, - }, - }, - }, - false, - }, - - { - "foo ${foo(42)}", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "foo": ast.Function{ - ArgTypes: []ast.Type{ast.TypeString}, - ReturnType: ast.TypeString, - }, - }, - }, - false, - }, - - { - `foo ${foo("42", 42)}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "foo": ast.Function{ - ArgTypes: []ast.Type{ast.TypeString}, - Variadic: true, - VariadicType: ast.TypeString, - ReturnType: ast.TypeString, - }, - }, - }, - false, - }, - } - - for _, tc := range cases { - node, err := Parse(tc.Input) - if err != nil { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - - // Modify the scope to add our conversion functions. - if tc.Scope.FuncMap == nil { - tc.Scope.FuncMap = make(map[string]ast.Function) - } - tc.Scope.FuncMap["intToString"] = ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - ReturnType: ast.TypeString, - } - - // Do the first pass... - visitor := &TypeCheck{Scope: tc.Scope, Implicit: implicitMap} - err = visitor.Visit(node) - if err != nil != tc.Error { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - if err != nil { - continue - } - - // If we didn't error, then the next type check should not fail - // WITHOUT implicits. - visitor = &TypeCheck{Scope: tc.Scope} - err = visitor.Visit(node) - if err != nil { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - } -} diff --git a/config/lang/eval.go b/config/lang/eval.go deleted file mode 100644 index 9ec583e41..000000000 --- a/config/lang/eval.go +++ /dev/null @@ -1,222 +0,0 @@ -package lang - -import ( - "bytes" - "fmt" - "sync" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -// EvalConfig is the configuration for evaluating. -type EvalConfig struct { - // GlobalScope is the global scope of execution for evaluation. - GlobalScope *ast.BasicScope - - // SemanticChecks is a list of additional semantic checks that will be run - // on the tree prior to evaluating it. The type checker, identifier checker, - // etc. will be run before these automatically. - SemanticChecks []SemanticChecker -} - -// SemanticChecker is the type that must be implemented to do a -// semantic check on an AST tree. This will be called with the root node. -type SemanticChecker func(ast.Node) error - -// Eval evaluates the given AST tree and returns its output value, the type -// of the output, and any error that occurred. -func Eval(root ast.Node, config *EvalConfig) (interface{}, ast.Type, error) { - // Copy the scope so we can add our builtins - if config == nil { - config = new(EvalConfig) - } - scope := registerBuiltins(config.GlobalScope) - implicitMap := map[ast.Type]map[ast.Type]string{ - ast.TypeFloat: { - ast.TypeInt: "__builtin_FloatToInt", - ast.TypeString: "__builtin_FloatToString", - }, - ast.TypeInt: { - ast.TypeFloat: "__builtin_IntToFloat", - ast.TypeString: "__builtin_IntToString", - }, - ast.TypeString: { - ast.TypeInt: "__builtin_StringToInt", - }, - } - - // Build our own semantic checks that we always run - tv := &TypeCheck{Scope: scope, Implicit: implicitMap} - ic := &IdentifierCheck{Scope: scope} - - // Build up the semantic checks for execution - checks := make( - []SemanticChecker, - len(config.SemanticChecks), - len(config.SemanticChecks)+2) - copy(checks, config.SemanticChecks) - checks = append(checks, ic.Visit) - checks = append(checks, tv.Visit) - - // Run the semantic checks - for _, check := range checks { - if err := check(root); err != nil { - return nil, ast.TypeInvalid, err - } - } - - // Execute - v := &evalVisitor{Scope: scope} - return v.Visit(root) -} - -// EvalNode is the interface that must be implemented by any ast.Node -// to support evaluation. This will be called in visitor pattern order. -// The result of each call to Eval is automatically pushed onto the -// stack as a LiteralNode. Pop elements off the stack to get child -// values. -type EvalNode interface { - Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) -} - -type evalVisitor struct { - Scope ast.Scope - Stack ast.Stack - - err error - lock sync.Mutex -} - -func (v *evalVisitor) Visit(root ast.Node) (interface{}, ast.Type, error) { - // Run the actual visitor pattern - root.Accept(v.visit) - - // Get our result and clear out everything else - var result *ast.LiteralNode - if v.Stack.Len() > 0 { - result = v.Stack.Pop().(*ast.LiteralNode) - } else { - result = new(ast.LiteralNode) - } - resultErr := v.err - - // Clear everything else so we aren't just dangling - v.Stack.Reset() - v.err = nil - - t, err := result.Type(v.Scope) - if err != nil { - return nil, ast.TypeInvalid, err - } - - return result.Value, t, resultErr -} - -func (v *evalVisitor) visit(raw ast.Node) ast.Node { - if v.err != nil { - return raw - } - - en, err := evalNode(raw) - if err != nil { - v.err = err - return raw - } - - out, outType, err := en.Eval(v.Scope, &v.Stack) - if err != nil { - v.err = err - return raw - } - - v.Stack.Push(&ast.LiteralNode{ - Value: out, - Typex: outType, - }) - return raw -} - -// evalNode is a private function that returns an EvalNode for built-in -// types as well as any other EvalNode implementations. -func evalNode(raw ast.Node) (EvalNode, error) { - switch n := raw.(type) { - case *ast.Call: - return &evalCall{n}, nil - case *ast.Concat: - return &evalConcat{n}, nil - case *ast.LiteralNode: - return &evalLiteralNode{n}, nil - case *ast.VariableAccess: - return &evalVariableAccess{n}, nil - default: - en, ok := n.(EvalNode) - if !ok { - return nil, fmt.Errorf("node doesn't support evaluation: %#v", raw) - } - - return en, nil - } -} - -type evalCall struct{ *ast.Call } - -func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) { - // Look up the function in the map - function, ok := s.LookupFunc(v.Func) - if !ok { - return nil, ast.TypeInvalid, fmt.Errorf( - "unknown function called: %s", v.Func) - } - - // The arguments are on the stack in reverse order, so pop them off. - args := make([]interface{}, len(v.Args)) - for i, _ := range v.Args { - node := stack.Pop().(*ast.LiteralNode) - args[len(v.Args)-1-i] = node.Value - } - - // Call the function - result, err := function.Callback(args) - if err != nil { - return nil, ast.TypeInvalid, fmt.Errorf("%s: %s", v.Func, err) - } - - return result, function.ReturnType, nil -} - -type evalConcat struct{ *ast.Concat } - -func (v *evalConcat) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) { - // The expressions should all be on the stack in reverse - // order. So pop them off, reverse their order, and concatenate. - nodes := make([]*ast.LiteralNode, 0, len(v.Exprs)) - for range v.Exprs { - nodes = append(nodes, stack.Pop().(*ast.LiteralNode)) - } - - var buf bytes.Buffer - for i := len(nodes) - 1; i >= 0; i-- { - buf.WriteString(nodes[i].Value.(string)) - } - - return buf.String(), ast.TypeString, nil -} - -type evalLiteralNode struct{ *ast.LiteralNode } - -func (v *evalLiteralNode) Eval(ast.Scope, *ast.Stack) (interface{}, ast.Type, error) { - return v.Value, v.Typex, nil -} - -type evalVariableAccess struct{ *ast.VariableAccess } - -func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, ast.Type, error) { - // Look up the variable in the map - variable, ok := scope.LookupVar(v.Name) - if !ok { - return nil, ast.TypeInvalid, fmt.Errorf( - "unknown variable accessed: %s", v.Name) - } - - return variable.Value, variable.Type, nil -} diff --git a/config/lang/eval_test.go b/config/lang/eval_test.go deleted file mode 100644 index 001963014..000000000 --- a/config/lang/eval_test.go +++ /dev/null @@ -1,335 +0,0 @@ -package lang - -import ( - "reflect" - "strconv" - "testing" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -func TestEval(t *testing.T) { - cases := []struct { - Input string - Scope *ast.BasicScope - Error bool - Result interface{} - ResultType ast.Type - }{ - { - "foo", - nil, - false, - "foo", - ast.TypeString, - }, - - { - "foo $${bar}", - nil, - false, - "foo ${bar}", - ast.TypeString, - }, - - { - "foo ${bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: "baz", - Type: ast.TypeString, - }, - }, - }, - false, - "foo baz", - ast.TypeString, - }, - - { - "foo ${42+1}", - nil, - false, - "foo 43", - ast.TypeString, - }, - - { - "foo ${42-1}", - nil, - false, - "foo 41", - ast.TypeString, - }, - - { - "foo ${42*2}", - nil, - false, - "foo 84", - ast.TypeString, - }, - - { - "foo ${42/2}", - nil, - false, - "foo 21", - ast.TypeString, - }, - - { - "foo ${42%4}", - nil, - false, - "foo 2", - ast.TypeString, - }, - - { - "foo ${42.0+1.0}", - nil, - false, - "foo 43", - ast.TypeString, - }, - - { - "foo ${42.0+1}", - nil, - false, - "foo 43", - ast.TypeString, - }, - - { - "foo ${42+1.0}", - nil, - false, - "foo 43", - ast.TypeString, - }, - - { - "foo ${42+2*2}", - nil, - false, - "foo 88", - ast.TypeString, - }, - - { - "foo ${42+(2*2)}", - nil, - false, - "foo 46", - ast.TypeString, - }, - - { - "foo ${bar+1}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 41, - Type: ast.TypeInt, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - { - "foo ${bar+1}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: "41", - Type: ast.TypeString, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - { - "foo ${bar+baz}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: "41", - Type: ast.TypeString, - }, - "baz": ast.Variable{ - Value: "1", - Type: ast.TypeString, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - { - "foo ${rand()}", - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.Function{ - ReturnType: ast.TypeString, - Callback: func([]interface{}) (interface{}, error) { - return "42", nil - }, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - { - `foo ${rand("foo", "bar")}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "rand": ast.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, - }, - - // Testing implicit type conversions - - { - "foo ${bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 42, - Type: ast.TypeInt, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - { - `foo ${foo("42")}`, - &ast.BasicScope{ - FuncMap: map[string]ast.Function{ - "foo": ast.Function{ - ArgTypes: []ast.Type{ast.TypeInt}, - ReturnType: ast.TypeString, - Callback: func(args []interface{}) (interface{}, error) { - return strconv.FormatInt(int64(args[0].(int)), 10), nil - }, - }, - }, - }, - false, - "foo 42", - ast.TypeString, - }, - - // Multiline - { - "foo ${42+\n1.0}", - nil, - false, - "foo 43", - ast.TypeString, - }, - - { - "foo ${-46}", - nil, - false, - "foo -46", - ast.TypeString, - }, - - { - "foo ${-46 + 5}", - nil, - false, - "foo -41", - ast.TypeString, - }, - - { - "foo ${46 + -5}", - nil, - false, - "foo 41", - ast.TypeString, - }, - - { - "foo ${-bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 41, - Type: ast.TypeInt, - }, - }, - }, - false, - "foo -41", - ast.TypeString, - }, - - { - "foo ${5 + -bar}", - &ast.BasicScope{ - VarMap: map[string]ast.Variable{ - "bar": ast.Variable{ - Value: 41, - Type: ast.TypeInt, - }, - }, - }, - false, - "foo -36", - ast.TypeString, - }, - } - - for _, tc := range cases { - node, err := Parse(tc.Input) - if err != nil { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - - out, outType, err := Eval(node, &EvalConfig{GlobalScope: tc.Scope}) - if err != nil != tc.Error { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - if outType != tc.ResultType { - t.Fatalf("Bad: %s\n\nInput: %s", outType, tc.Input) - } - if !reflect.DeepEqual(out, tc.Result) { - t.Fatalf("Bad: %#v\n\nInput: %s", out, tc.Input) - } - } -} diff --git a/config/lang/lang.y b/config/lang/lang.y deleted file mode 100644 index f55f7bf98..000000000 --- a/config/lang/lang.y +++ /dev/null @@ -1,173 +0,0 @@ -// This is the yacc input for creating the parser for interpolation -// expressions in Go. To build it, just run `go generate` on this -// package, as the lexer has the go generate pragma within it. - -%{ -package lang - -import ( - "github.com/hashicorp/terraform/config/lang/ast" -) - -%} - -%union { - node ast.Node - nodeList []ast.Node - str string - token *parserToken -} - -%token PROGRAM_BRACKET_LEFT PROGRAM_BRACKET_RIGHT -%token PROGRAM_STRING_START PROGRAM_STRING_END -%token PAREN_LEFT PAREN_RIGHT COMMA - -%token ARITH_OP IDENTIFIER INTEGER FLOAT STRING - -%type expr interpolation literal literalModeTop literalModeValue -%type args - -%left ARITH_OP - -%% - -top: - { - parserResult = &ast.LiteralNode{ - Value: "", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - } - } -| literalModeTop - { - parserResult = $1 - - // We want to make sure that the top value is always a Concat - // so that the return value is always a string type from an - // interpolation. - // - // The logic for checking for a LiteralNode is a little annoying - // because functionally the AST is the same, but we do that because - // 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.Typex != ast.TypeString { - parserResult = &ast.Concat{ - Exprs: []ast.Node{$1}, - Posx: $1.Pos(), - } - } - } - } - -literalModeTop: - literalModeValue - { - $$ = $1 - } -| literalModeTop literalModeValue - { - var result []ast.Node - if c, ok := $1.(*ast.Concat); ok { - result = append(c.Exprs, $2) - } else { - result = []ast.Node{$1, $2} - } - - $$ = &ast.Concat{ - Exprs: result, - Posx: result[0].Pos(), - } - } - -literalModeValue: - literal - { - $$ = $1 - } -| interpolation - { - $$ = $1 - } - -interpolation: - PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT - { - $$ = $2 - } - -expr: - PAREN_LEFT expr PAREN_RIGHT - { - $$ = $2 - } -| literalModeTop - { - $$ = $1 - } -| INTEGER - { - $$ = &ast.LiteralNode{ - Value: $1.Value.(int), - Typex: ast.TypeInt, - Posx: $1.Pos, - } - } -| FLOAT - { - $$ = &ast.LiteralNode{ - Value: $1.Value.(float64), - Typex: ast.TypeFloat, - Posx: $1.Pos, - } - } -| expr ARITH_OP expr - { - $$ = &ast.Arithmetic{ - Op: $2.Value.(ast.ArithmeticOp), - Exprs: []ast.Node{$1, $3}, - Posx: $1.Pos(), - } - } -| ARITH_OP expr - { - $$ = &ast.UnaryArithmetic{ - Op: $1.Value.(ast.ArithmeticOp), - Expr: $2, - Posx: $1.Pos, - } - } -| IDENTIFIER - { - $$ = &ast.VariableAccess{Name: $1.Value.(string), Posx: $1.Pos} - } -| IDENTIFIER PAREN_LEFT args PAREN_RIGHT - { - $$ = &ast.Call{Func: $1.Value.(string), Args: $3, Posx: $1.Pos} - } - -args: - { - $$ = nil - } -| args COMMA expr - { - $$ = append($1, $3) - } -| expr - { - $$ = append($$, $1) - } - -literal: - STRING - { - $$ = &ast.LiteralNode{ - Value: $1.Value.(string), - Typex: ast.TypeString, - Posx: $1.Pos, - } - } - -%% diff --git a/config/lang/lex.go b/config/lang/lex.go deleted file mode 100644 index 0dbe16c9f..000000000 --- a/config/lang/lex.go +++ /dev/null @@ -1,403 +0,0 @@ -package lang - -import ( - "bytes" - "fmt" - "strconv" - "unicode" - "unicode/utf8" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -//go:generate go tool yacc -p parser lang.y - -// The parser expects the lexer to return 0 on EOF. -const lexEOF = 0 - -// The parser uses the type Lex as a lexer. It must provide -// the methods Lex(*SymType) int and Error(string). -type parserLex struct { - Err error - Input string - - mode parserMode - interpolationDepth int - pos int - width int - col, line int - lastLine int - astPos *ast.Pos -} - -// parserToken is the token yielded to the parser. The value can be -// determined within the parser type based on the enum value returned -// from Lex. -type parserToken struct { - Value interface{} - Pos ast.Pos -} - -// parserMode keeps track of what mode we're in for the parser. We have -// two modes: literal and interpolation. Literal mode is when strings -// don't have to be quoted, and interpolations are defined as ${foo}. -// Interpolation mode means that strings have to be quoted and unquoted -// things are identifiers, such as foo("bar"). -type parserMode uint8 - -const ( - parserModeInvalid parserMode = 0 - parserModeLiteral = 1 << iota - parserModeInterpolation -) - -// The parser calls this method to get each new token. -func (x *parserLex) Lex(yylval *parserSymType) int { - // We always start in literal mode, since programs don't start - // in an interpolation. ex. "foo ${bar}" vs "bar" (and assuming interp.) - if x.mode == parserModeInvalid { - x.mode = parserModeLiteral - } - - // Defer an update to set the proper column/line we read the next token. - defer func() { - if yylval.token != nil && yylval.token.Pos.Column == 0 { - yylval.token.Pos = *x.astPos - } - }() - - x.astPos = nil - return x.lex(yylval) -} - -func (x *parserLex) lex(yylval *parserSymType) int { - switch x.mode { - case parserModeLiteral: - return x.lexModeLiteral(yylval) - case parserModeInterpolation: - return x.lexModeInterpolation(yylval) - default: - x.Error(fmt.Sprintf("Unknown parse mode: %d", x.mode)) - return lexEOF - } -} - -func (x *parserLex) lexModeLiteral(yylval *parserSymType) int { - for { - c := x.next() - if c == lexEOF { - return lexEOF - } - - // Are we starting an interpolation? - if c == '$' && x.peek() == '{' { - x.next() - x.interpolationDepth++ - x.mode = parserModeInterpolation - return PROGRAM_BRACKET_LEFT - } - - // We're just a normal string that isn't part of any interpolation yet. - x.backup() - result, terminated := x.lexString(yylval, x.interpolationDepth > 0) - - // If the string terminated and we're within an interpolation already - // then that means that we finished a nested string, so pop - // back out to interpolation mode. - if terminated && x.interpolationDepth > 0 { - x.mode = parserModeInterpolation - - // If the string is empty, just skip it. We're still in - // an interpolation so we do this to avoid empty nodes. - if yylval.token.Value.(string) == "" { - return x.lex(yylval) - } - } - - return result - } -} - -func (x *parserLex) lexModeInterpolation(yylval *parserSymType) int { - for { - c := x.next() - if c == lexEOF { - return lexEOF - } - - // Ignore all whitespace - if unicode.IsSpace(c) { - continue - } - - // If we see a double quote then we're lexing a string since - // we're in interpolation mode. - if c == '"' { - result, terminated := x.lexString(yylval, true) - if !terminated { - // The string didn't end, which means that we're in the - // middle of starting another interpolation. - x.mode = parserModeLiteral - - // If the string is empty and we're starting an interpolation, - // then just skip it to avoid empty string AST nodes - if yylval.token.Value.(string) == "" { - return x.lex(yylval) - } - } - - return result - } - - // If we are seeing a number, it is the start of a number. Lex it. - if c >= '0' && c <= '9' { - x.backup() - return x.lexNumber(yylval) - } - - switch c { - case '}': - // '}' means we ended the interpolation. Pop back into - // literal mode and reduce our interpolation depth. - x.interpolationDepth-- - x.mode = parserModeLiteral - return PROGRAM_BRACKET_RIGHT - case '(': - return PAREN_LEFT - case ')': - return PAREN_RIGHT - case ',': - return COMMA - case '+': - yylval.token = &parserToken{Value: ast.ArithmeticOpAdd} - return ARITH_OP - case '-': - yylval.token = &parserToken{Value: ast.ArithmeticOpSub} - return ARITH_OP - case '*': - yylval.token = &parserToken{Value: ast.ArithmeticOpMul} - return ARITH_OP - case '/': - yylval.token = &parserToken{Value: ast.ArithmeticOpDiv} - return ARITH_OP - case '%': - yylval.token = &parserToken{Value: ast.ArithmeticOpMod} - return ARITH_OP - default: - x.backup() - return x.lexId(yylval) - } - } -} - -func (x *parserLex) lexId(yylval *parserSymType) int { - var b bytes.Buffer - var last rune - for { - c := x.next() - if c == lexEOF { - break - } - - // We only allow * after a '.' for resource splast: type.name.*.id - // Otherwise, its probably multiplication. - if c == '*' && last != '.' { - x.backup() - break - } - - // If this isn't a character we want in an ID, return out. - // One day we should make this a regexp. - if c != '_' && - c != '-' && - c != '.' && - c != '*' && - !unicode.IsLetter(c) && - !unicode.IsNumber(c) { - x.backup() - break - } - - if _, err := b.WriteRune(c); err != nil { - x.Error(err.Error()) - return lexEOF - } - - last = c - } - - yylval.token = &parserToken{Value: b.String()} - return IDENTIFIER -} - -// lexNumber lexes out a number: an integer or a float. -func (x *parserLex) lexNumber(yylval *parserSymType) int { - var b bytes.Buffer - gotPeriod := false - for { - c := x.next() - if c == lexEOF { - break - } - - // If we see a period, we might be getting a float.. - if c == '.' { - // If we've already seen a period, then ignore it, and - // exit. This will probably result in a syntax error later. - if gotPeriod { - x.backup() - break - } - - gotPeriod = true - } else if c < '0' || c > '9' { - // If we're not seeing a number, then also exit. - x.backup() - break - } - - if _, err := b.WriteRune(c); err != nil { - x.Error(fmt.Sprintf("internal error: %s", err)) - return lexEOF - } - } - - // If we didn't see a period, it is an int - if !gotPeriod { - v, err := strconv.ParseInt(b.String(), 0, 0) - if err != nil { - x.Error(fmt.Sprintf("expected number: %s", err)) - return lexEOF - } - - yylval.token = &parserToken{Value: int(v)} - return INTEGER - } - - // If we did see a period, it is a float - f, err := strconv.ParseFloat(b.String(), 64) - if err != nil { - x.Error(fmt.Sprintf("expected float: %s", err)) - return lexEOF - } - - yylval.token = &parserToken{Value: f} - return FLOAT -} - -func (x *parserLex) lexString(yylval *parserSymType, quoted bool) (int, bool) { - var b bytes.Buffer - terminated := false - for { - c := x.next() - if c == lexEOF { - if quoted { - x.Error("unterminated string") - } - - break - } - - // Behavior is a bit different if we're lexing within a quoted string. - if quoted { - // If its a double quote, we've reached the end of the string - if c == '"' { - terminated = true - break - } - - // Let's check to see if we're escaping anything. - if c == '\\' { - switch n := x.next(); n { - case '\\', '"': - c = n - case 'n': - c = '\n' - default: - x.backup() - } - } - } - - // If we hit a dollar sign, then check if we're starting - // another interpolation. If so, then we're done. - if c == '$' { - n := x.peek() - - // If it is '{', then we're starting another interpolation - if n == '{' { - x.backup() - break - } - - // If it is '$', then we're escaping a dollar sign - if n == '$' { - x.next() - } - } - - if _, err := b.WriteRune(c); err != nil { - x.Error(err.Error()) - return lexEOF, false - } - } - - yylval.token = &parserToken{Value: b.String()} - return STRING, terminated -} - -// Return the next rune for the lexer. -func (x *parserLex) next() rune { - if int(x.pos) >= len(x.Input) { - x.width = 0 - return lexEOF - } - - r, w := utf8.DecodeRuneInString(x.Input[x.pos:]) - x.width = w - x.pos += x.width - - if x.line == 0 { - x.line = 1 - x.col = 1 - } else { - x.col += 1 - } - - if r == '\n' { - x.lastLine = x.col - x.line += 1 - x.col = 1 - } - - if x.astPos == nil { - x.astPos = &ast.Pos{Column: x.col, Line: x.line} - } - - return r -} - -// peek returns but does not consume the next rune in the input -func (x *parserLex) peek() rune { - r := x.next() - x.backup() - return r -} - -// backup steps back one rune. Can only be called once per next. -func (x *parserLex) backup() { - x.pos -= x.width - x.col -= 1 - - // If we are at column 0, we're backing up across a line boundary - // so we need to be careful to get the proper value. - if x.col == 0 { - x.col = x.lastLine - x.line -= 1 - } -} - -// The parser calls this method on a parse error. -func (x *parserLex) Error(s string) { - x.Err = fmt.Errorf("parse error: %s", s) -} diff --git a/config/lang/lex_test.go b/config/lang/lex_test.go deleted file mode 100644 index 572aa0f53..000000000 --- a/config/lang/lex_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package lang - -import ( - "reflect" - "testing" -) - -func TestLex(t *testing.T) { - cases := []struct { - Input string - Output []int - }{ - { - "foo", - []int{STRING, lexEOF}, - }, - - { - "foo$bar", - []int{STRING, lexEOF}, - }, - - { - "foo ${bar}", - []int{STRING, PROGRAM_BRACKET_LEFT, IDENTIFIER, PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "foo $${bar}", - []int{STRING, lexEOF}, - }, - - { - "foo $$$${bar}", - []int{STRING, lexEOF}, - }, - - { - "foo ${\"bar\"}", - []int{STRING, PROGRAM_BRACKET_LEFT, STRING, PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(baz)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, IDENTIFIER, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(baz, foo)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, - IDENTIFIER, COMMA, IDENTIFIER, - PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(42)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, INTEGER, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(-42)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, ARITH_OP, INTEGER, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(-42.0)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, ARITH_OP, FLOAT, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(42+1)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, - INTEGER, ARITH_OP, INTEGER, - PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(42+-1)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, - INTEGER, ARITH_OP, ARITH_OP, INTEGER, - PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(3.14159)}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, FLOAT, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "${bar(inner(baz))}", - []int{PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, - IDENTIFIER, PAREN_LEFT, - IDENTIFIER, - PAREN_RIGHT, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "foo ${foo.bar.baz}", - []int{STRING, PROGRAM_BRACKET_LEFT, IDENTIFIER, PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "foo ${foo.bar.*.baz}", - []int{STRING, PROGRAM_BRACKET_LEFT, IDENTIFIER, PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - "foo ${foo(\"baz\")}", - []int{STRING, PROGRAM_BRACKET_LEFT, - IDENTIFIER, PAREN_LEFT, STRING, PAREN_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - - { - `foo ${"${var.foo}"}`, - []int{STRING, PROGRAM_BRACKET_LEFT, - PROGRAM_BRACKET_LEFT, IDENTIFIER, PROGRAM_BRACKET_RIGHT, - PROGRAM_BRACKET_RIGHT, lexEOF}, - }, - } - - for _, tc := range cases { - l := &parserLex{Input: tc.Input} - var actual []int - for { - token := l.Lex(new(parserSymType)) - actual = append(actual, token) - - if token == lexEOF { - break - } - - // Be careful against what are probably infinite loops - if len(actual) > 100 { - t.Fatalf("Input:%s\n\nExausted.", tc.Input) - } - } - - if !reflect.DeepEqual(actual, tc.Output) { - t.Fatalf( - "Input: %s\n\nBad: %#v\n\nExpected: %#v", - tc.Input, actual, tc.Output) - } - } -} diff --git a/config/lang/parse.go b/config/lang/parse.go deleted file mode 100644 index 8ece590af..000000000 --- a/config/lang/parse.go +++ /dev/null @@ -1,32 +0,0 @@ -package lang - -import ( - "sync" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -var parserErrors []error -var parserLock sync.Mutex -var parserResult ast.Node - -// Parse parses the given program and returns an executable AST tree. -func Parse(v string) (ast.Node, error) { - // Unfortunately due to the way that goyacc generated parsers are - // formatted, we can only do a single parse at a time without a lot - // of extra work. In the future we can remove this limitation. - parserLock.Lock() - defer parserLock.Unlock() - - // Reset our globals - parserErrors = nil - parserResult = nil - - // Create the lexer - lex := &parserLex{Input: v} - - // Parse! - parserParse(lex) - - return parserResult, lex.Err -} diff --git a/config/lang/parse_test.go b/config/lang/parse_test.go deleted file mode 100644 index dc75424bc..000000000 --- a/config/lang/parse_test.go +++ /dev/null @@ -1,363 +0,0 @@ -package lang - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -func TestParse(t *testing.T) { - cases := []struct { - Input string - Error bool - Result ast.Node - }{ - { - "", - false, - &ast.LiteralNode{ - Value: "", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - }, - - { - "foo", - false, - &ast.LiteralNode{ - Value: "foo", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - }, - - { - "$${var.foo}", - false, - &ast.LiteralNode{ - Value: "${var.foo}", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - }, - - { - "foo ${var.bar}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.VariableAccess{ - Name: "var.bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - - { - "foo ${var.bar} baz", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.VariableAccess{ - Name: "var.bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.LiteralNode{ - Value: " baz", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 15, Line: 1}, - }, - }, - }, - }, - - { - "foo ${\"bar\"}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.LiteralNode{ - Value: "bar", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - - { - `foo ${func('baz')}`, - true, - nil, - }, - - { - "foo ${42}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.LiteralNode{ - Value: 42, - Typex: ast.TypeInt, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - - { - "foo ${3.14159}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.LiteralNode{ - Value: 3.14159, - Typex: ast.TypeFloat, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - - { - "foo ${42+1}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.Arithmetic{ - Op: ast.ArithmeticOpAdd, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: 42, - Typex: ast.TypeInt, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.LiteralNode{ - Value: 1, - Typex: ast.TypeInt, - Posx: ast.Pos{Column: 10, Line: 1}, - }, - }, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - - { - "foo ${var.bar*1} baz", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.Arithmetic{ - Op: ast.ArithmeticOpMul, - Exprs: []ast.Node{ - &ast.VariableAccess{ - Name: "var.bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.LiteralNode{ - Value: 1, - Typex: ast.TypeInt, - Posx: ast.Pos{Column: 15, Line: 1}, - }, - }, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.LiteralNode{ - Value: " baz", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 17, Line: 1}, - }, - }, - }, - }, - - { - "${foo()}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 3, Line: 1}, - Exprs: []ast.Node{ - &ast.Call{ - Func: "foo", - Args: nil, - Posx: ast.Pos{Column: 3, Line: 1}, - }, - }, - }, - }, - - { - "${foo(bar)}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 3, Line: 1}, - Exprs: []ast.Node{ - &ast.Call{ - Func: "foo", - Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ - &ast.VariableAccess{ - Name: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - }, - }, - }, - }, - }, - - { - "${foo(bar, baz)}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 3, Line: 1}, - Exprs: []ast.Node{ - &ast.Call{ - Func: "foo", - Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ - &ast.VariableAccess{ - Name: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.VariableAccess{ - Name: "baz", - Posx: ast.Pos{Column: 11, Line: 1}, - }, - }, - }, - }, - }, - }, - - { - "${foo(bar(baz))}", - false, - &ast.Concat{ - Posx: ast.Pos{Column: 3, Line: 1}, - Exprs: []ast.Node{ - &ast.Call{ - Func: "foo", - Posx: ast.Pos{Column: 3, Line: 1}, - Args: []ast.Node{ - &ast.Call{ - Func: "bar", - Posx: ast.Pos{Column: 7, Line: 1}, - Args: []ast.Node{ - &ast.VariableAccess{ - Name: "baz", - Posx: ast.Pos{Column: 11, Line: 1}, - }, - }, - }, - }, - }, - }, - }, - }, - - { - `foo ${"bar ${baz}"}`, - false, - &ast.Concat{ - Posx: ast.Pos{Column: 1, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "foo ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - }, - &ast.Concat{ - Posx: ast.Pos{Column: 7, Line: 1}, - Exprs: []ast.Node{ - &ast.LiteralNode{ - Value: "bar ", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 7, Line: 1}, - }, - &ast.VariableAccess{ - Name: "baz", - Posx: ast.Pos{Column: 14, Line: 1}, - }, - }, - }, - }, - }, - }, - - { - `foo ${bar ${baz}}`, - true, - nil, - }, - - { - `foo ${${baz}}`, - true, - nil, - }, - - { - "${var", - true, - nil, - }, - } - - for _, tc := range cases { - actual, err := Parse(tc.Input) - if err != nil != tc.Error { - t.Fatalf("Error: %s\n\nInput: %s", err, tc.Input) - } - if !reflect.DeepEqual(actual, tc.Result) { - t.Fatalf("Bad: %#v\n\nInput: %s", actual, tc.Input) - } - } -} diff --git a/config/lang/token.go b/config/lang/token.go deleted file mode 100644 index 48e87f869..000000000 --- a/config/lang/token.go +++ /dev/null @@ -1 +0,0 @@ -package lang diff --git a/config/lang/transform_fixed.go b/config/lang/transform_fixed.go deleted file mode 100644 index c551423df..000000000 --- a/config/lang/transform_fixed.go +++ /dev/null @@ -1,26 +0,0 @@ -package lang - -import ( - "github.com/hashicorp/terraform/config/lang/ast" -) - -// FixedValueTransform transforms an AST to return a fixed value for -// all interpolations. i.e. you can make "hello ${anything}" always -// turn into "hello foo". -func FixedValueTransform(root ast.Node, Value *ast.LiteralNode) ast.Node { - // We visit the nodes in top-down order - result := root - switch n := result.(type) { - case *ast.Concat: - for i, v := range n.Exprs { - n.Exprs[i] = FixedValueTransform(v, Value) - } - case *ast.LiteralNode: - // We keep it as-is - default: - // Anything else we replace - result = Value - } - - return result -} diff --git a/config/lang/transform_fixed_test.go b/config/lang/transform_fixed_test.go deleted file mode 100644 index c5e5a1ca3..000000000 --- a/config/lang/transform_fixed_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package lang - -import ( - "reflect" - "testing" - - "github.com/hashicorp/terraform/config/lang/ast" -) - -func TestFixedValueTransform(t *testing.T) { - cases := []struct { - Input ast.Node - Output ast.Node - }{ - { - &ast.LiteralNode{Value: 42}, - &ast.LiteralNode{Value: 42}, - }, - - { - &ast.VariableAccess{Name: "bar"}, - &ast.LiteralNode{Value: "foo"}, - }, - - { - &ast.Concat{ - Exprs: []ast.Node{ - &ast.VariableAccess{Name: "bar"}, - &ast.LiteralNode{Value: 42}, - }, - }, - &ast.Concat{ - Exprs: []ast.Node{ - &ast.LiteralNode{Value: "foo"}, - &ast.LiteralNode{Value: 42}, - }, - }, - }, - } - - value := &ast.LiteralNode{Value: "foo"} - for _, tc := range cases { - actual := FixedValueTransform(tc.Input, value) - if !reflect.DeepEqual(actual, tc.Output) { - t.Fatalf("bad: %#v\n\nInput: %#v", actual, tc.Input) - } - } -} diff --git a/config/lang/y.go b/config/lang/y.go deleted file mode 100644 index faffd55d3..000000000 --- a/config/lang/y.go +++ /dev/null @@ -1,632 +0,0 @@ -//line lang.y:6 -package lang - -import __yyfmt__ "fmt" - -//line lang.y:6 -import ( - "github.com/hashicorp/terraform/config/lang/ast" -) - -//line lang.y:14 -type parserSymType struct { - yys int - node ast.Node - nodeList []ast.Node - str string - token *parserToken -} - -const PROGRAM_BRACKET_LEFT = 57346 -const PROGRAM_BRACKET_RIGHT = 57347 -const PROGRAM_STRING_START = 57348 -const PROGRAM_STRING_END = 57349 -const PAREN_LEFT = 57350 -const PAREN_RIGHT = 57351 -const COMMA = 57352 -const ARITH_OP = 57353 -const IDENTIFIER = 57354 -const INTEGER = 57355 -const FLOAT = 57356 -const STRING = 57357 - -var parserToknames = [...]string{ - "$end", - "error", - "$unk", - "PROGRAM_BRACKET_LEFT", - "PROGRAM_BRACKET_RIGHT", - "PROGRAM_STRING_START", - "PROGRAM_STRING_END", - "PAREN_LEFT", - "PAREN_RIGHT", - "COMMA", - "ARITH_OP", - "IDENTIFIER", - "INTEGER", - "FLOAT", - "STRING", -} -var parserStatenames = [...]string{} - -const parserEofCode = 1 -const parserErrCode = 2 -const parserMaxDepth = 200 - -//line lang.y:173 - -//line yacctab:1 -var parserExca = [...]int{ - -1, 1, - 1, -1, - -2, 0, -} - -const parserNprod = 20 -const parserPrivate = 57344 - -var parserTokenNames []string -var parserStates []string - -const parserLast = 34 - -var parserAct = [...]int{ - - 9, 7, 3, 16, 22, 8, 17, 17, 20, 17, - 1, 18, 6, 23, 8, 19, 25, 26, 21, 11, - 2, 24, 7, 4, 5, 0, 10, 27, 0, 14, - 15, 12, 13, 6, -} -var parserPact = [...]int{ - - -3, -1000, -3, -1000, -1000, -1000, -1000, 18, -1000, -2, - 18, -3, -1000, -1000, 18, 0, -1000, 18, -5, -1000, - 18, -1000, -1000, 7, -4, -1000, 18, -4, -} -var parserPgo = [...]int{ - - 0, 0, 24, 23, 19, 2, 13, 10, -} -var parserR1 = [...]int{ - - 0, 7, 7, 4, 4, 5, 5, 2, 1, 1, - 1, 1, 1, 1, 1, 1, 6, 6, 6, 3, -} -var parserR2 = [...]int{ - - 0, 0, 1, 1, 2, 1, 1, 3, 3, 1, - 1, 1, 3, 2, 1, 4, 0, 3, 1, 1, -} -var parserChk = [...]int{ - - -1000, -7, -4, -5, -3, -2, 15, 4, -5, -1, - 8, -4, 13, 14, 11, 12, 5, 11, -1, -1, - 8, -1, 9, -6, -1, 9, 10, -1, -} -var parserDef = [...]int{ - - 1, -2, 2, 3, 5, 6, 19, 0, 4, 0, - 0, 9, 10, 11, 0, 14, 7, 0, 0, 13, - 16, 12, 8, 0, 18, 15, 0, 17, -} -var parserTok1 = [...]int{ - - 1, -} -var parserTok2 = [...]int{ - - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, -} -var parserTok3 = [...]int{ - 0, -} - -var parserErrorMessages = [...]struct { - state int - token int - msg string -}{} - -//line yaccpar:1 - -/* parser for yacc output */ - -var ( - parserDebug = 0 - parserErrorVerbose = false -) - -type parserLexer interface { - Lex(lval *parserSymType) int - Error(s string) -} - -type parserParser interface { - Parse(parserLexer) int - Lookahead() int -} - -type parserParserImpl struct { - lookahead func() int -} - -func (p *parserParserImpl) Lookahead() int { - return p.lookahead() -} - -func parserNewParser() parserParser { - p := &parserParserImpl{ - lookahead: func() int { return -1 }, - } - return p -} - -const parserFlag = -1000 - -func parserTokname(c int) string { - if c >= 1 && c-1 < len(parserToknames) { - if parserToknames[c-1] != "" { - return parserToknames[c-1] - } - } - return __yyfmt__.Sprintf("tok-%v", c) -} - -func parserStatname(s int) string { - if s >= 0 && s < len(parserStatenames) { - if parserStatenames[s] != "" { - return parserStatenames[s] - } - } - return __yyfmt__.Sprintf("state-%v", s) -} - -func parserErrorMessage(state, lookAhead int) string { - const TOKSTART = 4 - - if !parserErrorVerbose { - return "syntax error" - } - - for _, e := range parserErrorMessages { - if e.state == state && e.token == lookAhead { - return "syntax error: " + e.msg - } - } - - res := "syntax error: unexpected " + parserTokname(lookAhead) - - // To match Bison, suggest at most four expected tokens. - expected := make([]int, 0, 4) - - // Look for shiftable tokens. - base := parserPact[state] - for tok := TOKSTART; tok-1 < len(parserToknames); tok++ { - if n := base + tok; n >= 0 && n < parserLast && parserChk[parserAct[n]] == tok { - if len(expected) == cap(expected) { - return res - } - expected = append(expected, tok) - } - } - - if parserDef[state] == -2 { - i := 0 - for parserExca[i] != -1 || parserExca[i+1] != state { - i += 2 - } - - // Look for tokens that we accept or reduce. - for i += 2; parserExca[i] >= 0; i += 2 { - tok := parserExca[i] - if tok < TOKSTART || parserExca[i+1] == 0 { - continue - } - if len(expected) == cap(expected) { - return res - } - expected = append(expected, tok) - } - - // If the default action is to accept or reduce, give up. - if parserExca[i+1] != 0 { - return res - } - } - - for i, tok := range expected { - if i == 0 { - res += ", expecting " - } else { - res += " or " - } - res += parserTokname(tok) - } - return res -} - -func parserlex1(lex parserLexer, lval *parserSymType) (char, token int) { - token = 0 - char = lex.Lex(lval) - if char <= 0 { - token = parserTok1[0] - goto out - } - if char < len(parserTok1) { - token = parserTok1[char] - goto out - } - if char >= parserPrivate { - if char < parserPrivate+len(parserTok2) { - token = parserTok2[char-parserPrivate] - goto out - } - } - for i := 0; i < len(parserTok3); i += 2 { - token = parserTok3[i+0] - if token == char { - token = parserTok3[i+1] - goto out - } - } - -out: - if token == 0 { - token = parserTok2[1] /* unknown char */ - } - if parserDebug >= 3 { - __yyfmt__.Printf("lex %s(%d)\n", parserTokname(token), uint(char)) - } - return char, token -} - -func parserParse(parserlex parserLexer) int { - return parserNewParser().Parse(parserlex) -} - -func (parserrcvr *parserParserImpl) Parse(parserlex parserLexer) int { - var parsern int - var parserlval parserSymType - var parserVAL parserSymType - var parserDollar []parserSymType - _ = parserDollar // silence set and not used - parserS := make([]parserSymType, parserMaxDepth) - - Nerrs := 0 /* number of errors */ - Errflag := 0 /* error recovery flag */ - parserstate := 0 - parserchar := -1 - parsertoken := -1 // parserchar translated into internal numbering - parserrcvr.lookahead = func() int { return parserchar } - defer func() { - // Make sure we report no lookahead when not parsing. - parserstate = -1 - parserchar = -1 - parsertoken = -1 - }() - parserp := -1 - goto parserstack - -ret0: - return 0 - -ret1: - return 1 - -parserstack: - /* put a state and value onto the stack */ - if parserDebug >= 4 { - __yyfmt__.Printf("char %v in %v\n", parserTokname(parsertoken), parserStatname(parserstate)) - } - - parserp++ - if parserp >= len(parserS) { - nyys := make([]parserSymType, len(parserS)*2) - copy(nyys, parserS) - parserS = nyys - } - parserS[parserp] = parserVAL - parserS[parserp].yys = parserstate - -parsernewstate: - parsern = parserPact[parserstate] - if parsern <= parserFlag { - goto parserdefault /* simple state */ - } - if parserchar < 0 { - parserchar, parsertoken = parserlex1(parserlex, &parserlval) - } - parsern += parsertoken - if parsern < 0 || parsern >= parserLast { - goto parserdefault - } - parsern = parserAct[parsern] - if parserChk[parsern] == parsertoken { /* valid shift */ - parserchar = -1 - parsertoken = -1 - parserVAL = parserlval - parserstate = parsern - if Errflag > 0 { - Errflag-- - } - goto parserstack - } - -parserdefault: - /* default state action */ - parsern = parserDef[parserstate] - if parsern == -2 { - if parserchar < 0 { - parserchar, parsertoken = parserlex1(parserlex, &parserlval) - } - - /* look through exception table */ - xi := 0 - for { - if parserExca[xi+0] == -1 && parserExca[xi+1] == parserstate { - break - } - xi += 2 - } - for xi += 2; ; xi += 2 { - parsern = parserExca[xi+0] - if parsern < 0 || parsern == parsertoken { - break - } - } - parsern = parserExca[xi+1] - if parsern < 0 { - goto ret0 - } - } - if parsern == 0 { - /* error ... attempt to resume parsing */ - switch Errflag { - case 0: /* brand new error */ - parserlex.Error(parserErrorMessage(parserstate, parsertoken)) - Nerrs++ - if parserDebug >= 1 { - __yyfmt__.Printf("%s", parserStatname(parserstate)) - __yyfmt__.Printf(" saw %s\n", parserTokname(parsertoken)) - } - fallthrough - - case 1, 2: /* incompletely recovered error ... try again */ - Errflag = 3 - - /* find a state where "error" is a legal shift action */ - for parserp >= 0 { - parsern = parserPact[parserS[parserp].yys] + parserErrCode - if parsern >= 0 && parsern < parserLast { - parserstate = parserAct[parsern] /* simulate a shift of "error" */ - if parserChk[parserstate] == parserErrCode { - goto parserstack - } - } - - /* the current p has no shift on "error", pop stack */ - if parserDebug >= 2 { - __yyfmt__.Printf("error recovery pops state %d\n", parserS[parserp].yys) - } - parserp-- - } - /* there is no state on the stack with an error shift ... abort */ - goto ret1 - - case 3: /* no shift yet; clobber input char */ - if parserDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", parserTokname(parsertoken)) - } - if parsertoken == parserEofCode { - goto ret1 - } - parserchar = -1 - parsertoken = -1 - goto parsernewstate /* try again in the same state */ - } - } - - /* reduction by production parsern */ - if parserDebug >= 2 { - __yyfmt__.Printf("reduce %v in:\n\t%v\n", parsern, parserStatname(parserstate)) - } - - parsernt := parsern - parserpt := parserp - _ = parserpt // guard against "declared and not used" - - parserp -= parserR2[parsern] - // parserp is now the index of $0. Perform the default action. Iff the - // reduced production is ε, $1 is possibly out of range. - if parserp+1 >= len(parserS) { - nyys := make([]parserSymType, len(parserS)*2) - copy(nyys, parserS) - parserS = nyys - } - parserVAL = parserS[parserp+1] - - /* consult goto table to find next state */ - parsern = parserR1[parsern] - parserg := parserPgo[parsern] - parserj := parserg + parserS[parserp].yys + 1 - - if parserj >= parserLast { - parserstate = parserAct[parserg] - } else { - parserstate = parserAct[parserj] - if parserChk[parserstate] != -parsern { - parserstate = parserAct[parserg] - } - } - // dummy call; replaced with literal code - switch parsernt { - - case 1: - parserDollar = parserS[parserpt-0 : parserpt+1] - //line lang.y:35 - { - parserResult = &ast.LiteralNode{ - Value: "", - Typex: ast.TypeString, - Posx: ast.Pos{Column: 1, Line: 1}, - } - } - case 2: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:43 - { - parserResult = parserDollar[1].node - - // We want to make sure that the top value is always a Concat - // so that the return value is always a string type from an - // interpolation. - // - // The logic for checking for a LiteralNode is a little annoying - // because functionally the AST is the same, but we do that because - // it makes for an easy literal check later (to check if a string - // has any interpolations). - if _, ok := parserDollar[1].node.(*ast.Concat); !ok { - if n, ok := parserDollar[1].node.(*ast.LiteralNode); !ok || n.Typex != ast.TypeString { - parserResult = &ast.Concat{ - Exprs: []ast.Node{parserDollar[1].node}, - Posx: parserDollar[1].node.Pos(), - } - } - } - } - case 3: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:66 - { - parserVAL.node = parserDollar[1].node - } - case 4: - parserDollar = parserS[parserpt-2 : parserpt+1] - //line lang.y:70 - { - var result []ast.Node - if c, ok := parserDollar[1].node.(*ast.Concat); ok { - result = append(c.Exprs, parserDollar[2].node) - } else { - result = []ast.Node{parserDollar[1].node, parserDollar[2].node} - } - - parserVAL.node = &ast.Concat{ - Exprs: result, - Posx: result[0].Pos(), - } - } - case 5: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:86 - { - parserVAL.node = parserDollar[1].node - } - case 6: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:90 - { - parserVAL.node = parserDollar[1].node - } - case 7: - parserDollar = parserS[parserpt-3 : parserpt+1] - //line lang.y:96 - { - parserVAL.node = parserDollar[2].node - } - case 8: - parserDollar = parserS[parserpt-3 : parserpt+1] - //line lang.y:102 - { - parserVAL.node = parserDollar[2].node - } - case 9: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:106 - { - parserVAL.node = parserDollar[1].node - } - case 10: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:110 - { - parserVAL.node = &ast.LiteralNode{ - Value: parserDollar[1].token.Value.(int), - Typex: ast.TypeInt, - Posx: parserDollar[1].token.Pos, - } - } - case 11: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:118 - { - parserVAL.node = &ast.LiteralNode{ - Value: parserDollar[1].token.Value.(float64), - Typex: ast.TypeFloat, - Posx: parserDollar[1].token.Pos, - } - } - case 12: - parserDollar = parserS[parserpt-3 : parserpt+1] - //line lang.y:126 - { - parserVAL.node = &ast.Arithmetic{ - Op: parserDollar[2].token.Value.(ast.ArithmeticOp), - Exprs: []ast.Node{parserDollar[1].node, parserDollar[3].node}, - Posx: parserDollar[1].node.Pos(), - } - } - case 13: - parserDollar = parserS[parserpt-2 : parserpt+1] - //line lang.y:134 - { - parserVAL.node = &ast.UnaryArithmetic{ - Op: parserDollar[1].token.Value.(ast.ArithmeticOp), - Expr: parserDollar[2].node, - Posx: parserDollar[1].token.Pos, - } - } - case 14: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:142 - { - parserVAL.node = &ast.VariableAccess{Name: parserDollar[1].token.Value.(string), Posx: parserDollar[1].token.Pos} - } - case 15: - parserDollar = parserS[parserpt-4 : parserpt+1] - //line lang.y:146 - { - parserVAL.node = &ast.Call{Func: parserDollar[1].token.Value.(string), Args: parserDollar[3].nodeList, Posx: parserDollar[1].token.Pos} - } - case 16: - parserDollar = parserS[parserpt-0 : parserpt+1] - //line lang.y:151 - { - parserVAL.nodeList = nil - } - case 17: - parserDollar = parserS[parserpt-3 : parserpt+1] - //line lang.y:155 - { - parserVAL.nodeList = append(parserDollar[1].nodeList, parserDollar[3].node) - } - case 18: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:159 - { - parserVAL.nodeList = append(parserVAL.nodeList, parserDollar[1].node) - } - case 19: - parserDollar = parserS[parserpt-1 : parserpt+1] - //line lang.y:165 - { - parserVAL.node = &ast.LiteralNode{ - Value: parserDollar[1].token.Value.(string), - Typex: ast.TypeString, - Posx: parserDollar[1].token.Pos, - } - } - } - goto parserstack /* stack new state and value */ -} diff --git a/config/lang/y.output b/config/lang/y.output deleted file mode 100644 index 998d2673c..000000000 --- a/config/lang/y.output +++ /dev/null @@ -1,293 +0,0 @@ - -state 0 - $accept: .top $end - top: . (1) - - PROGRAM_BRACKET_LEFT shift 7 - STRING shift 6 - . reduce 1 (src line 34) - - interpolation goto 5 - literal goto 4 - literalModeTop goto 2 - literalModeValue goto 3 - top goto 1 - -state 1 - $accept: top.$end - - $end accept - . error - - -state 2 - top: literalModeTop. (2) - literalModeTop: literalModeTop.literalModeValue - - PROGRAM_BRACKET_LEFT shift 7 - STRING shift 6 - . reduce 2 (src line 42) - - interpolation goto 5 - literal goto 4 - literalModeValue goto 8 - -state 3 - literalModeTop: literalModeValue. (3) - - . reduce 3 (src line 64) - - -state 4 - literalModeValue: literal. (5) - - . reduce 5 (src line 84) - - -state 5 - literalModeValue: interpolation. (6) - - . reduce 6 (src line 89) - - -state 6 - literal: STRING. (19) - - . reduce 19 (src line 163) - - -state 7 - interpolation: PROGRAM_BRACKET_LEFT.expr PROGRAM_BRACKET_RIGHT - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . error - - expr goto 9 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - -state 8 - literalModeTop: literalModeTop literalModeValue. (4) - - . reduce 4 (src line 69) - - -state 9 - interpolation: PROGRAM_BRACKET_LEFT expr.PROGRAM_BRACKET_RIGHT - expr: expr.ARITH_OP expr - - PROGRAM_BRACKET_RIGHT shift 16 - ARITH_OP shift 17 - . error - - -state 10 - expr: PAREN_LEFT.expr PAREN_RIGHT - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . error - - expr goto 18 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - -state 11 - literalModeTop: literalModeTop.literalModeValue - expr: literalModeTop. (9) - - PROGRAM_BRACKET_LEFT shift 7 - STRING shift 6 - . reduce 9 (src line 105) - - interpolation goto 5 - literal goto 4 - literalModeValue goto 8 - -state 12 - expr: INTEGER. (10) - - . reduce 10 (src line 109) - - -state 13 - expr: FLOAT. (11) - - . reduce 11 (src line 117) - - -state 14 - expr: ARITH_OP.expr - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . error - - expr goto 19 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - -state 15 - expr: IDENTIFIER. (14) - expr: IDENTIFIER.PAREN_LEFT args PAREN_RIGHT - - PAREN_LEFT shift 20 - . reduce 14 (src line 141) - - -state 16 - interpolation: PROGRAM_BRACKET_LEFT expr PROGRAM_BRACKET_RIGHT. (7) - - . reduce 7 (src line 94) - - -state 17 - expr: expr ARITH_OP.expr - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . error - - expr goto 21 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - -state 18 - expr: PAREN_LEFT expr.PAREN_RIGHT - expr: expr.ARITH_OP expr - - PAREN_RIGHT shift 22 - ARITH_OP shift 17 - . error - - -state 19 - expr: expr.ARITH_OP expr - expr: ARITH_OP expr. (13) - - . reduce 13 (src line 133) - - -state 20 - expr: IDENTIFIER PAREN_LEFT.args PAREN_RIGHT - args: . (16) - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . reduce 16 (src line 150) - - expr goto 24 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - args goto 23 - -state 21 - expr: expr.ARITH_OP expr - expr: expr ARITH_OP expr. (12) - - . reduce 12 (src line 125) - - -state 22 - expr: PAREN_LEFT expr PAREN_RIGHT. (8) - - . reduce 8 (src line 100) - - -state 23 - expr: IDENTIFIER PAREN_LEFT args.PAREN_RIGHT - args: args.COMMA expr - - PAREN_RIGHT shift 25 - COMMA shift 26 - . error - - -state 24 - expr: expr.ARITH_OP expr - args: expr. (18) - - ARITH_OP shift 17 - . reduce 18 (src line 158) - - -state 25 - expr: IDENTIFIER PAREN_LEFT args PAREN_RIGHT. (15) - - . reduce 15 (src line 145) - - -state 26 - args: args COMMA.expr - - PROGRAM_BRACKET_LEFT shift 7 - PAREN_LEFT shift 10 - ARITH_OP shift 14 - IDENTIFIER shift 15 - INTEGER shift 12 - FLOAT shift 13 - STRING shift 6 - . error - - expr goto 27 - interpolation goto 5 - literal goto 4 - literalModeTop goto 11 - literalModeValue goto 3 - -state 27 - expr: expr.ARITH_OP expr - args: args COMMA expr. (17) - - ARITH_OP shift 17 - . reduce 17 (src line 154) - - -15 terminals, 8 nonterminals -20 grammar rules, 28/2000 states -0 shift/reduce, 0 reduce/reduce conflicts reported -57 working sets used -memory: parser 40/30000 -23 extra closures -57 shift entries, 1 exceptions -15 goto entries -27 entries saved by goto default -Optimizer space used: output 34/30000 -34 table entries, 2 zero -maximum spread: 15, maximum offset: 26 diff --git a/config/raw_config.go b/config/raw_config.go index 2a66288d1..efd3ab0b5 100644 --- a/config/raw_config.go +++ b/config/raw_config.go @@ -5,8 +5,8 @@ import ( "encoding/gob" "sync" - "github.com/hashicorp/terraform/config/lang" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil" + "github.com/hashicorp/hil/ast" "github.com/mitchellh/copystructure" "github.com/mitchellh/reflectwalk" ) @@ -127,7 +127,7 @@ func (r *RawConfig) Interpolate(vs map[string]ast.Variable) error { // None of the variables we need are computed, meaning we should // be able to properly evaluate. - out, _, err := lang.Eval(root, config) + out, _, err := hil.Eval(root, config) if err != nil { return "", err } @@ -298,7 +298,7 @@ type gobRawConfig struct { } // langEvalConfig returns the evaluation configuration we use to execute. -func langEvalConfig(vs map[string]ast.Variable) *lang.EvalConfig { +func langEvalConfig(vs map[string]ast.Variable) *hil.EvalConfig { funcMap := make(map[string]ast.Function) for k, v := range Funcs() { funcMap[k] = v @@ -307,7 +307,7 @@ func langEvalConfig(vs map[string]ast.Variable) *lang.EvalConfig { funcMap["keys"] = interpolationFuncKeys(vs) funcMap["values"] = interpolationFuncValues(vs) - return &lang.EvalConfig{ + return &hil.EvalConfig{ GlobalScope: &ast.BasicScope{ VarMap: vs, FuncMap: funcMap, diff --git a/config/raw_config_test.go b/config/raw_config_test.go index 81d3f20c1..d6901d3c4 100644 --- a/config/raw_config_test.go +++ b/config/raw_config_test.go @@ -5,7 +5,7 @@ import ( "reflect" "testing" - "github.com/hashicorp/terraform/config/lang/ast" + "github.com/hashicorp/hil/ast" ) func TestNewRawConfig(t *testing.T) {