From d12bf664036c46cc8a9492acda12b16bd2a8f92e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 14 Jan 2015 11:59:06 -0800 Subject: [PATCH] config/lang: implicit builtins are coming in --- config/lang/builtins.go | 24 ++++++++++++++++++++++++ config/lang/check_types.go | 4 +++- config/lang/engine.go | 24 +++++++++++++++++++++--- config/lang/engine_test.go | 17 +++++++++++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 config/lang/builtins.go diff --git a/config/lang/builtins.go b/config/lang/builtins.go new file mode 100644 index 000000000..7472389f0 --- /dev/null +++ b/config/lang/builtins.go @@ -0,0 +1,24 @@ +package lang + +import ( + "strconv" + + "github.com/hashicorp/terraform/config/lang/ast" +) + +func registerBuiltins(scope *Scope) { + if scope.FuncMap == nil { + scope.FuncMap = make(map[string]Function) + } + scope.FuncMap["__builtin_IntToString"] = builtinIntToString() +} + +func builtinIntToString() Function { + return Function{ + ArgTypes: []ast.Type{ast.TypeInt}, + ReturnType: ast.TypeString, + Callback: func(args []interface{}) (interface{}, error) { + return strconv.FormatInt(int64(args[0].(int)), 10), nil + }, + } +} diff --git a/config/lang/check_types.go b/config/lang/check_types.go index 1a0fb1b8d..9aec9af19 100644 --- a/config/lang/check_types.go +++ b/config/lang/check_types.go @@ -155,7 +155,9 @@ func (v *TypeCheck) visitVariableAccess(n *ast.VariableAccess) { } func (v *TypeCheck) createErr(n ast.Node, str string) { - v.err = fmt.Errorf("%s: %s", n.Pos(), str) + pos := n.Pos() + v.err = fmt.Errorf("At column %d, line %d: %s", + pos.Column, pos.Line, str) } func (v *TypeCheck) implicitConversion( diff --git a/config/lang/engine.go b/config/lang/engine.go index 594048ed9..f54d6a9d3 100644 --- a/config/lang/engine.go +++ b/config/lang/engine.go @@ -27,9 +27,17 @@ type SemanticChecker func(ast.Node) error // Execute executes the given ast.Node and returns its final value, its // type, and an error if one exists. func (e *Engine) Execute(root ast.Node) (interface{}, ast.Type, error) { + // Copy the scope so we can add our builtins + scope := e.scope() + implicitMap := map[ast.Type]map[ast.Type]string{ + ast.TypeInt: { + ast.TypeString: "__builtin_IntToString", + }, + } + // Build our own semantic checks that we always run - tv := &TypeCheck{Scope: e.GlobalScope} - ic := &IdentifierCheck{Scope: e.GlobalScope} + tv := &TypeCheck{Scope: scope, Implicit: implicitMap} + ic := &IdentifierCheck{Scope: scope} // Build up the semantic checks for execution checks := make( @@ -46,10 +54,20 @@ func (e *Engine) Execute(root ast.Node) (interface{}, ast.Type, error) { } // Execute - v := &executeVisitor{Scope: e.GlobalScope} + v := &executeVisitor{Scope: scope} return v.Visit(root) } +func (e *Engine) scope() *Scope { + var scope Scope + if e.GlobalScope != nil { + scope = *e.GlobalScope + } + + registerBuiltins(&scope) + return &scope +} + // executeVisitor is the visitor used to do the actual execution of // a program. Note at this point it is assumed that the types check out // and the identifiers exist. diff --git a/config/lang/engine_test.go b/config/lang/engine_test.go index e5a840cbc..1a5a7b4c5 100644 --- a/config/lang/engine_test.go +++ b/config/lang/engine_test.go @@ -77,6 +77,23 @@ func TestEngineExecute(t *testing.T) { "foo foobar", ast.TypeString, }, + + // Testing implicit type conversions + + { + "foo ${bar}", + &Scope{ + VarMap: map[string]Variable{ + "bar": Variable{ + Value: 42, + Type: ast.TypeInt, + }, + }, + }, + false, + "foo 42", + ast.TypeString, + }, } for _, tc := range cases {