Merge pull request #5804 from hashicorp/dep-update-hil

deps: Update github.com/hashicorp/hil
This commit is contained in:
James Nugent 2016-03-23 08:46:28 -07:00
commit 9cb08cb229
8 changed files with 207 additions and 81 deletions

4
Godeps/Godeps.json generated
View File

@ -738,11 +738,11 @@
}, },
{ {
"ImportPath": "github.com/hashicorp/hil", "ImportPath": "github.com/hashicorp/hil",
"Rev": "1586b586f59cfa528a751d4a62be88910d34e6e9" "Rev": "59cce4313fb7be2d9064afbdb3cacd76737cfa3c"
}, },
{ {
"ImportPath": "github.com/hashicorp/hil/ast", "ImportPath": "github.com/hashicorp/hil/ast",
"Rev": "1586b586f59cfa528a751d4a62be88910d34e6e9" "Rev": "59cce4313fb7be2d9064afbdb3cacd76737cfa3c"
}, },
{ {
"ImportPath": "github.com/hashicorp/logutils", "ImportPath": "github.com/hashicorp/logutils",

18
vendor/github.com/hashicorp/hil/appveyor.yml generated vendored Normal file
View File

@ -0,0 +1,18 @@
version: "build-{branch}-{build}"
image: Visual Studio 2015
clone_folder: c:\gopath\src\github.com\hashicorp\hil
environment:
GOPATH: c:\gopath
init:
- git config --global core.autocrlf true
install:
- cmd: >-
echo %Path%
go version
go env
go get -d -v -t ./...
build_script:
- cmd: go test -v ./...

View File

@ -53,4 +53,5 @@ const (
TypeInt TypeInt
TypeFloat TypeFloat
TypeList TypeList
TypeMap
) )

View File

@ -34,33 +34,39 @@ func (n *Index) Type(s Scope) (Type, error) {
if !ok { if !ok {
return TypeInvalid, fmt.Errorf("unknown variable accessed: %s", variableAccess.Name) return TypeInvalid, fmt.Errorf("unknown variable accessed: %s", variableAccess.Name)
} }
if variable.Type != TypeList {
switch variable.Type {
case TypeList:
return n.typeList(variable, variableAccess.Name)
case TypeMap:
return n.typeMap(variable, variableAccess.Name)
default:
return TypeInvalid, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type) return TypeInvalid, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type)
} }
}
func (n *Index) typeList(variable Variable, variableName string) (Type, error) {
// We assume type checking has already determined that this is a list
list := variable.Value.([]Variable) list := variable.Value.([]Variable)
// Ensure that the types of the list elements are homogenous return VariableListElementTypesAreHomogenous(variableName, list)
listTypes := make(map[Type]struct{}) }
for _, v := range list {
if _, ok := listTypes[v.Type]; ok {
continue
}
listTypes[v.Type] = struct{}{}
}
if len(listTypes) != 1 { func (n *Index) typeMap(variable Variable, variableName string) (Type, error) {
typesFound := make([]string, len(listTypes)) // We assume type checking has already determined that this is a map
i := 0 vmap := variable.Value.(map[string]Variable)
for k, _ := range listTypes {
typesFound[0] = k.String()
i++
}
types := strings.Join(typesFound, ", ")
return TypeInvalid, fmt.Errorf("list %q does not have homogenous types. found %s", variableAccess.Name, types)
}
return list[0].Type, nil return VariableMapValueTypesAreHomogenous(variableName, vmap)
}
func reportTypes(typesFound map[Type]struct{}) string {
stringTypes := make([]string, len(typesFound))
i := 0
for k, _ := range typesFound {
stringTypes[0] = k.String()
i++
}
return strings.Join(stringTypes, ", ")
} }
func (n *Index) GoString() string { func (n *Index) GoString() string {

View File

@ -0,0 +1,45 @@
package ast
import "fmt"
func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) {
listTypes := make(map[Type]struct{})
for _, v := range list {
if _, ok := listTypes[v.Type]; ok {
continue
}
listTypes[v.Type] = struct{}{}
}
if len(listTypes) != 1 && len(list) != 0 {
return TypeInvalid, fmt.Errorf("list %q does not have homogenous types. found %s", variableName, reportTypes(listTypes))
}
if len(list) > 0 {
return list[0].Type, nil
}
return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName)
}
func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) {
valueTypes := make(map[Type]struct{})
for _, v := range vmap {
if _, ok := valueTypes[v.Type]; ok {
continue
}
valueTypes[v.Type] = struct{}{}
}
if len(valueTypes) != 1 && len(vmap) != 0 {
return TypeInvalid, fmt.Errorf("map %q does not have homogenous value types. found %s", variableName, reportTypes(valueTypes))
}
// For loop here is an easy way to get a single key, we return immediately.
for _, v := range vmap {
return v.Type, nil
}
// This means the map is empty
return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName)
}

View File

@ -241,7 +241,13 @@ func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
types[len(n.Exprs)-1-i] = v.StackPop() types[len(n.Exprs)-1-i] = v.StackPop()
} }
// All concat args must be strings, so validate that // If there is only one argument and it is a list, we evaluate to a list
if len(types) == 1 && types[0] == ast.TypeList {
v.StackPush(ast.TypeList)
return n, nil
}
// Otherwise, all concat args must be strings, so validate that
for i, t := range types { for i, t := range types {
if t != ast.TypeString { if t != ast.TypeString {
cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
@ -251,7 +257,7 @@ func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
} }
return nil, fmt.Errorf( return nil, fmt.Errorf(
"output of an HIL expression must be a string (argument %d is %s)", i+1, t) "output of an HIL expression must be a string, or a single list (argument %d is %s)", i+1, t)
} }
} }
@ -293,15 +299,6 @@ type typeCheckIndex struct {
} }
func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) { func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) {
value, err := tc.n.Key.Type(v.Scope)
if err != nil {
return nil, err
}
if value != ast.TypeInt {
return nil, fmt.Errorf("key of an index must be an int, was %s", value)
}
// Ensure we have a VariableAccess as the target // Ensure we have a VariableAccess as the target
varAccessNode, ok := tc.n.Target.(*ast.VariableAccess) varAccessNode, ok := tc.n.Target.(*ast.VariableAccess)
if !ok { if !ok {
@ -313,28 +310,40 @@ func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) {
if !ok { if !ok {
return nil, fmt.Errorf("unknown variable accessed: %s", varAccessNode.Name) return nil, fmt.Errorf("unknown variable accessed: %s", varAccessNode.Name)
} }
if variable.Type != ast.TypeList {
keyType, err := tc.n.Key.Type(v.Scope)
if err != nil {
return nil, err
}
switch variable.Type {
case ast.TypeList:
if keyType != ast.TypeInt {
return nil, fmt.Errorf("key of an index must be an int, was %s", keyType)
}
valType, err := ast.VariableListElementTypesAreHomogenous(varAccessNode.Name, variable.Value.([]ast.Variable))
if err != nil {
return tc.n, err
}
v.StackPush(valType)
return tc.n, nil
case ast.TypeMap:
if keyType != ast.TypeString {
return nil, fmt.Errorf("key of an index must be a string, was %s", keyType)
}
valType, err := ast.VariableMapValueTypesAreHomogenous(varAccessNode.Name, variable.Value.(map[string]ast.Variable))
if err != nil {
return tc.n, err
}
v.StackPush(valType)
return tc.n, nil
default:
return nil, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type) return nil, fmt.Errorf("invalid index operation into non-indexable type: %s", variable.Type)
} }
list := variable.Value.([]ast.Variable)
// Ensure that the types of the list elements are homogenous
listTypes := make(map[ast.Type]struct{})
for _, v := range list {
if _, ok := listTypes[v.Type]; ok {
continue
}
listTypes[v.Type] = struct{}{}
}
if len(listTypes) != 1 {
return nil, fmt.Errorf("list %q does not have homogenous types (%s)", varAccessNode.Name)
}
// This is the type since the list is homogenous in type
v.StackPush(list[0].Type)
return tc.n, nil
} }
func (v *TypeCheck) ImplicitConversion( func (v *TypeCheck) ImplicitConversion(

View File

@ -200,16 +200,35 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
if err != nil { if err != nil {
return nil, ast.TypeInvalid, err return nil, ast.TypeInvalid, err
} }
key, keyType, err := evalKey.Eval(scope, stack) key, keyType, err := evalKey.Eval(scope, stack)
if err != nil {
// Last sanity check return nil, ast.TypeInvalid, err
if targetType != ast.TypeList {
return nil, ast.TypeInvalid, fmt.Errorf("target for indexing must be ast.TypeList, is %s", targetType)
}
if keyType != ast.TypeInt {
return nil, ast.TypeInvalid, fmt.Errorf("key for indexing must be ast.TypeInt, is %s", keyType)
} }
variableName := v.Index.Target.(*ast.VariableAccess).Name
switch targetType {
case ast.TypeList:
if keyType != ast.TypeInt {
return nil, ast.TypeInvalid, fmt.Errorf("key for indexing list %q must be an int, is %s", variableName, keyType)
}
return v.evalListIndex(variableName, target, key)
case ast.TypeMap:
if keyType != ast.TypeString {
return nil, ast.TypeInvalid, fmt.Errorf("key for indexing map %q must be a string, is %s", variableName, keyType)
}
return v.evalMapIndex(variableName, target, key)
default:
return nil, ast.TypeInvalid, fmt.Errorf("target %q for indexing must be ast.TypeList or ast.TypeMap, is %s", variableName, targetType)
}
}
func (v *evalIndex) evalListIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
// We assume type checking was already done and we can assume that target
// is a list and key is an int
list, ok := target.([]ast.Variable) list, ok := target.([]ast.Variable)
if !ok { if !ok {
return nil, ast.TypeInvalid, fmt.Errorf("cannot cast target to []Variable") return nil, ast.TypeInvalid, fmt.Errorf("cannot cast target to []Variable")
@ -225,7 +244,7 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
} }
if keyInt < 0 || len(list) < keyInt+1 { if keyInt < 0 || len(list) < keyInt+1 {
return nil, ast.TypeInvalid, fmt.Errorf("index %d out of range (max %d)", keyInt, len(list)) return nil, ast.TypeInvalid, fmt.Errorf("index %d out of range for list %s (max %d)", keyInt, variableName, len(list))
} }
returnVal := list[keyInt].Value returnVal := list[keyInt].Value
@ -234,6 +253,31 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
return returnVal, returnType, nil return returnVal, returnType, nil
} }
func (v *evalIndex) evalMapIndex(variableName string, target interface{}, key interface{}) (interface{}, ast.Type, error) {
// We assume type checking was already done and we can assume that target
// is a map and key is a string
vmap, ok := target.(map[string]ast.Variable)
if !ok {
return nil, ast.TypeInvalid, fmt.Errorf("cannot cast target to map[string]Variable")
}
keyString, ok := key.(string)
if !ok {
return nil, ast.TypeInvalid, fmt.Errorf("cannot cast key to string")
}
if len(vmap) == 0 {
return nil, ast.TypeInvalid, fmt.Errorf("map is empty")
}
value, ok := vmap[keyString]
if !ok {
return nil, ast.TypeInvalid, fmt.Errorf("key %q does not exist in map %s", keyString, variableName)
}
return value.Value, value.Type, nil
}
type evalConcat struct{ *ast.Concat } type evalConcat struct{ *ast.Concat }
func (v *evalConcat) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) { func (v *evalConcat) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, error) {
@ -244,6 +288,12 @@ func (v *evalConcat) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type,
nodes = append(nodes, stack.Pop().(*ast.LiteralNode)) nodes = append(nodes, stack.Pop().(*ast.LiteralNode))
} }
// Special case the single list
if len(nodes) == 1 && nodes[0].Typex == ast.TypeList {
return nodes[0].Value, ast.TypeList, nil
}
// Otherwise concatenate the strings
var buf bytes.Buffer var buf bytes.Buffer
for i := len(nodes) - 1; i >= 0; i-- { for i := len(nodes) - 1; i >= 0; i-- {
buf.WriteString(nodes[i].Value.(string)) buf.WriteString(nodes[i].Value.(string))

37
vendor/github.com/hashicorp/hil/y.go generated vendored
View File

@ -55,7 +55,7 @@ var parserStatenames = [...]string{}
const parserEofCode = 1 const parserEofCode = 1
const parserErrCode = 2 const parserErrCode = 2
const parserMaxDepth = 200 const parserInitialStackSize = 16
//line lang.y:196 //line lang.y:196
@ -157,18 +157,17 @@ type parserParser interface {
} }
type parserParserImpl struct { type parserParserImpl struct {
lookahead func() int lval parserSymType
stack [parserInitialStackSize]parserSymType
char int
} }
func (p *parserParserImpl) Lookahead() int { func (p *parserParserImpl) Lookahead() int {
return p.lookahead() return p.char
} }
func parserNewParser() parserParser { func parserNewParser() parserParser {
p := &parserParserImpl{ return &parserParserImpl{}
lookahead: func() int { return -1 },
}
return p
} }
const parserFlag = -1000 const parserFlag = -1000
@ -296,22 +295,20 @@ func parserParse(parserlex parserLexer) int {
func (parserrcvr *parserParserImpl) Parse(parserlex parserLexer) int { func (parserrcvr *parserParserImpl) Parse(parserlex parserLexer) int {
var parsern int var parsern int
var parserlval parserSymType
var parserVAL parserSymType var parserVAL parserSymType
var parserDollar []parserSymType var parserDollar []parserSymType
_ = parserDollar // silence set and not used _ = parserDollar // silence set and not used
parserS := make([]parserSymType, parserMaxDepth) parserS := parserrcvr.stack[:]
Nerrs := 0 /* number of errors */ Nerrs := 0 /* number of errors */
Errflag := 0 /* error recovery flag */ Errflag := 0 /* error recovery flag */
parserstate := 0 parserstate := 0
parserchar := -1 parserrcvr.char = -1
parsertoken := -1 // parserchar translated into internal numbering parsertoken := -1 // parserrcvr.char translated into internal numbering
parserrcvr.lookahead = func() int { return parserchar }
defer func() { defer func() {
// Make sure we report no lookahead when not parsing. // Make sure we report no lookahead when not parsing.
parserstate = -1 parserstate = -1
parserchar = -1 parserrcvr.char = -1
parsertoken = -1 parsertoken = -1
}() }()
parserp := -1 parserp := -1
@ -343,8 +340,8 @@ parsernewstate:
if parsern <= parserFlag { if parsern <= parserFlag {
goto parserdefault /* simple state */ goto parserdefault /* simple state */
} }
if parserchar < 0 { if parserrcvr.char < 0 {
parserchar, parsertoken = parserlex1(parserlex, &parserlval) parserrcvr.char, parsertoken = parserlex1(parserlex, &parserrcvr.lval)
} }
parsern += parsertoken parsern += parsertoken
if parsern < 0 || parsern >= parserLast { if parsern < 0 || parsern >= parserLast {
@ -352,9 +349,9 @@ parsernewstate:
} }
parsern = parserAct[parsern] parsern = parserAct[parsern]
if parserChk[parsern] == parsertoken { /* valid shift */ if parserChk[parsern] == parsertoken { /* valid shift */
parserchar = -1 parserrcvr.char = -1
parsertoken = -1 parsertoken = -1
parserVAL = parserlval parserVAL = parserrcvr.lval
parserstate = parsern parserstate = parsern
if Errflag > 0 { if Errflag > 0 {
Errflag-- Errflag--
@ -366,8 +363,8 @@ parserdefault:
/* default state action */ /* default state action */
parsern = parserDef[parserstate] parsern = parserDef[parserstate]
if parsern == -2 { if parsern == -2 {
if parserchar < 0 { if parserrcvr.char < 0 {
parserchar, parsertoken = parserlex1(parserlex, &parserlval) parserrcvr.char, parsertoken = parserlex1(parserlex, &parserrcvr.lval)
} }
/* look through exception table */ /* look through exception table */
@ -430,7 +427,7 @@ parserdefault:
if parsertoken == parserEofCode { if parsertoken == parserEofCode {
goto ret1 goto ret1
} }
parserchar = -1 parserrcvr.char = -1
parsertoken = -1 parsertoken = -1
goto parsernewstate /* try again in the same state */ goto parsernewstate /* try again in the same state */
} }