zclsyntax: parsing of the index operator

This can either be a traversal or a first-class node depending on whether
the given expression is a literal. This exception is made to allow
applications to conditionally populate only part of a potentially-large
collection if the config is only requesting one or two distinct indices.

In particular, it allows the following to be considered a single traversal
from the scope:

    foo.bar[0].baz
This commit is contained in:
Martin Atkins 2017-06-05 07:09:04 -07:00
parent 8fc5bd5141
commit bca573d3d0
3 changed files with 76 additions and 2 deletions

View File

@ -387,6 +387,31 @@ func (e *ConditionalExpr) StartRange() zcl.Range {
return e.Condition.StartRange()
}
type IndexExpr struct {
Collection Expression
Key Expression
SrcRange zcl.Range
OpenRange zcl.Range
}
func (e *IndexExpr) walkChildNodes(w internalWalkFunc) {
e.Collection = w(e.Collection).(Expression)
e.Key = w(e.Key).(Expression)
}
func (e *IndexExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
panic("IndexExpr.Value not yet implemented")
}
func (e *IndexExpr) Range() zcl.Range {
return e.SrcRange
}
func (e *IndexExpr) StartRange() zcl.Range {
return e.OpenRange
}
type TupleConsExpr struct {
Exprs []Expression

View File

@ -19,6 +19,10 @@ func (e *FunctionCallExpr) Variables() []zcl.Traversal {
return Variables(e)
}
func (e *IndexExpr) Variables() []zcl.Traversal {
return Variables(e)
}
func (e *LiteralValueExpr) Variables() []zcl.Traversal {
return Variables(e)
}

View File

@ -461,10 +461,11 @@ Traversal:
case TokenDot:
// Attribute access or splat
dot := p.Read()
attrTok := p.Read()
attrTok := p.Peek()
switch attrTok.Type {
case TokenIdent:
attrTok = p.Read() // eat token
name := string(attrTok.Bytes)
rng := zcl.RangeBetween(dot.Range, attrTok.Range)
step := zcl.TraverseAttr{
@ -491,7 +492,51 @@ Traversal:
// in order to support autocomplete triggered by typing a
// period.
p.setRecovery()
break Traversal
}
case TokenOBrack:
// Indexing of a collection.
// This may or may not be a zcl.Traverser, depending on whether
// the key value is something constant.
open := p.Read()
var close Token
p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets
keyExpr, keyDiags := p.ParseExpression()
diags = append(diags, keyDiags...)
if p.recovery && keyDiags.HasErrors() {
close = p.recover(TokenCBrack)
} else {
close = p.Read()
if close.Type != TokenCBrack && !p.recovery {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Missing close bracket on index",
Detail: "The index operator must end with a closing bracket (\"]\").",
Subject: &close.Range,
})
close = p.recover(TokenCBrack)
}
}
p.PushIncludeNewlines(true)
if lit, isLit := keyExpr.(*LiteralValueExpr); isLit {
litKey, _ := lit.Value(nil)
rng := zcl.RangeBetween(open.Range, close.Range)
step := &zcl.TraverseIndex{
Key: litKey,
SrcRange: rng,
}
ret = makeRelativeTraversal(ret, step, rng)
} else {
rng := zcl.RangeBetween(open.Range, close.Range)
ret = &IndexExpr{
Collection: ret,
Key: keyExpr,
SrcRange: rng,
OpenRange: open.Range,
}
}
default: