Merge pull request #5804 from hashicorp/dep-update-hil
deps: Update github.com/hashicorp/hil
This commit is contained in:
commit
9cb08cb229
|
@ -738,11 +738,11 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/hil",
|
||||
"Rev": "1586b586f59cfa528a751d4a62be88910d34e6e9"
|
||||
"Rev": "59cce4313fb7be2d9064afbdb3cacd76737cfa3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/hil/ast",
|
||||
"Rev": "1586b586f59cfa528a751d4a62be88910d34e6e9"
|
||||
"Rev": "59cce4313fb7be2d9064afbdb3cacd76737cfa3c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/logutils",
|
||||
|
|
|
@ -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 ./...
|
|
@ -53,4 +53,5 @@ const (
|
|||
TypeInt
|
||||
TypeFloat
|
||||
TypeList
|
||||
TypeMap
|
||||
)
|
||||
|
|
|
@ -34,33 +34,39 @@ func (n *Index) Type(s Scope) (Type, error) {
|
|||
if !ok {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// Ensure that the types of the list elements are homogenous
|
||||
listTypes := make(map[Type]struct{})
|
||||
for _, v := range list {
|
||||
if _, ok := listTypes[v.Type]; ok {
|
||||
continue
|
||||
}
|
||||
listTypes[v.Type] = struct{}{}
|
||||
}
|
||||
return VariableListElementTypesAreHomogenous(variableName, list)
|
||||
}
|
||||
|
||||
if len(listTypes) != 1 {
|
||||
typesFound := make([]string, len(listTypes))
|
||||
func (n *Index) typeMap(variable Variable, variableName string) (Type, error) {
|
||||
// We assume type checking has already determined that this is a map
|
||||
vmap := variable.Value.(map[string]Variable)
|
||||
|
||||
return VariableMapValueTypesAreHomogenous(variableName, vmap)
|
||||
}
|
||||
|
||||
func reportTypes(typesFound map[Type]struct{}) string {
|
||||
stringTypes := make([]string, len(typesFound))
|
||||
i := 0
|
||||
for k, _ := range listTypes {
|
||||
typesFound[0] = k.String()
|
||||
for k, _ := range typesFound {
|
||||
stringTypes[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 strings.Join(stringTypes, ", ")
|
||||
}
|
||||
|
||||
func (n *Index) GoString() string {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -241,7 +241,13 @@ func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
|||
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 {
|
||||
if t != ast.TypeString {
|
||||
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(
|
||||
"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) {
|
||||
|
||||
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
|
||||
varAccessNode, ok := tc.n.Target.(*ast.VariableAccess)
|
||||
if !ok {
|
||||
|
@ -313,28 +310,40 @@ func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
|||
if !ok {
|
||||
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)
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
|
@ -200,16 +200,35 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
|
|||
if err != nil {
|
||||
return nil, ast.TypeInvalid, err
|
||||
}
|
||||
|
||||
key, keyType, err := evalKey.Eval(scope, stack)
|
||||
|
||||
// Last sanity check
|
||||
if targetType != ast.TypeList {
|
||||
return nil, ast.TypeInvalid, fmt.Errorf("target for indexing must be ast.TypeList, is %s", targetType)
|
||||
if err != nil {
|
||||
return nil, ast.TypeInvalid, err
|
||||
}
|
||||
|
||||
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 must be ast.TypeInt, is %s", keyType)
|
||||
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)
|
||||
if !ok {
|
||||
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 {
|
||||
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
|
||||
|
@ -234,6 +253,31 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
|
|||
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 }
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
// 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
|
||||
for i := len(nodes) - 1; i >= 0; i-- {
|
||||
buf.WriteString(nodes[i].Value.(string))
|
||||
|
|
|
@ -55,7 +55,7 @@ var parserStatenames = [...]string{}
|
|||
|
||||
const parserEofCode = 1
|
||||
const parserErrCode = 2
|
||||
const parserMaxDepth = 200
|
||||
const parserInitialStackSize = 16
|
||||
|
||||
//line lang.y:196
|
||||
|
||||
|
@ -157,18 +157,17 @@ type parserParser interface {
|
|||
}
|
||||
|
||||
type parserParserImpl struct {
|
||||
lookahead func() int
|
||||
lval parserSymType
|
||||
stack [parserInitialStackSize]parserSymType
|
||||
char int
|
||||
}
|
||||
|
||||
func (p *parserParserImpl) Lookahead() int {
|
||||
return p.lookahead()
|
||||
return p.char
|
||||
}
|
||||
|
||||
func parserNewParser() parserParser {
|
||||
p := &parserParserImpl{
|
||||
lookahead: func() int { return -1 },
|
||||
}
|
||||
return p
|
||||
return &parserParserImpl{}
|
||||
}
|
||||
|
||||
const parserFlag = -1000
|
||||
|
@ -296,22 +295,20 @@ func parserParse(parserlex parserLexer) int {
|
|||
|
||||
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)
|
||||
parserS := parserrcvr.stack[:]
|
||||
|
||||
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 }
|
||||
parserrcvr.char = -1
|
||||
parsertoken := -1 // parserrcvr.char translated into internal numbering
|
||||
defer func() {
|
||||
// Make sure we report no lookahead when not parsing.
|
||||
parserstate = -1
|
||||
parserchar = -1
|
||||
parserrcvr.char = -1
|
||||
parsertoken = -1
|
||||
}()
|
||||
parserp := -1
|
||||
|
@ -343,8 +340,8 @@ parsernewstate:
|
|||
if parsern <= parserFlag {
|
||||
goto parserdefault /* simple state */
|
||||
}
|
||||
if parserchar < 0 {
|
||||
parserchar, parsertoken = parserlex1(parserlex, &parserlval)
|
||||
if parserrcvr.char < 0 {
|
||||
parserrcvr.char, parsertoken = parserlex1(parserlex, &parserrcvr.lval)
|
||||
}
|
||||
parsern += parsertoken
|
||||
if parsern < 0 || parsern >= parserLast {
|
||||
|
@ -352,9 +349,9 @@ parsernewstate:
|
|||
}
|
||||
parsern = parserAct[parsern]
|
||||
if parserChk[parsern] == parsertoken { /* valid shift */
|
||||
parserchar = -1
|
||||
parserrcvr.char = -1
|
||||
parsertoken = -1
|
||||
parserVAL = parserlval
|
||||
parserVAL = parserrcvr.lval
|
||||
parserstate = parsern
|
||||
if Errflag > 0 {
|
||||
Errflag--
|
||||
|
@ -366,8 +363,8 @@ parserdefault:
|
|||
/* default state action */
|
||||
parsern = parserDef[parserstate]
|
||||
if parsern == -2 {
|
||||
if parserchar < 0 {
|
||||
parserchar, parsertoken = parserlex1(parserlex, &parserlval)
|
||||
if parserrcvr.char < 0 {
|
||||
parserrcvr.char, parsertoken = parserlex1(parserlex, &parserrcvr.lval)
|
||||
}
|
||||
|
||||
/* look through exception table */
|
||||
|
@ -430,7 +427,7 @@ parserdefault:
|
|||
if parsertoken == parserEofCode {
|
||||
goto ret1
|
||||
}
|
||||
parserchar = -1
|
||||
parserrcvr.char = -1
|
||||
parsertoken = -1
|
||||
goto parsernewstate /* try again in the same state */
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue