config/lang: implicit builtins are coming in

This commit is contained in:
Mitchell Hashimoto 2015-01-14 11:59:06 -08:00
parent 36b6601baf
commit d12bf66403
4 changed files with 65 additions and 4 deletions

24
config/lang/builtins.go Normal file
View File

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

View File

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

View File

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

View File

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