zclsyntax: parsing of "attribute-only" splat expressions

Attribute-only splat expressions, indicated by using the star symbol as
if it were an attribute, are inherited from HIL and are a limited sort of
splat that only works for walking through attributes in each item of its
source.

Later zcl will also get full splat expressions, whose syntax is using
the star symbol as if it were an _index_, which will generalize to
supporting _all_ postfix traversal operations, including indexing and
nested splats.
This commit is contained in:
Martin Atkins 2017-06-16 08:33:35 -07:00
parent 78db663590
commit 22bc5ce5c6
2 changed files with 101 additions and 1 deletions

View File

@ -486,6 +486,50 @@ upper(
0,
},
{
`[{name: "Steve"}, {name: "Ermintrude"}].*.name`,
nil,
cty.TupleVal([]cty.Value{
cty.StringVal("Steve"),
cty.StringVal("Ermintrude"),
}),
0,
},
{
`{name: "Steve"}.*.name`,
nil,
cty.TupleVal([]cty.Value{
cty.StringVal("Steve"),
}),
0,
},
{
`["hello", "goodbye"].*`,
nil,
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("goodbye"),
}),
0,
},
{
`"hello".*`,
nil,
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
}),
0,
},
{
// For an "attribute-only" splat, an index operator applies to
// the splat result as a whole, rather than being incorporated
// into the splat traversal itself.
`[{name: "Steve"}, {name: "Ermintrude"}].*.name[0]`,
nil,
cty.StringVal("Steve"),
0,
},
{
`["hello"][0]`,
nil,

View File

@ -498,7 +498,61 @@ Traversal:
ret = makeRelativeTraversal(ret, step, rng)
// TODO: TokenStar, for splats
case TokenStar:
// "Attribute-only" splat expression.
// (This is a kinda weird construct inherited from HIL, which
// behaves a bit like a [*] splat except that it is only able
// to do attribute traversals into each of its elements,
// whereas foo[*] can support _any_ traversal.
marker := p.Read() // eat star
trav := make(zcl.Traversal, 0, 1)
var firstRange, lastRange zcl.Range
firstRange = p.NextRange()
for p.Peek().Type == TokenDot {
dot := p.Read()
if p.Peek().Type != TokenIdent {
if !p.recovery {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Invalid attribute name",
Detail: "An attribute name is required after a dot.",
Subject: &attrTok.Range,
})
}
p.setRecovery()
continue Traversal
}
attrTok := p.Read()
trav = append(trav, zcl.TraverseAttr{
Name: string(attrTok.Bytes),
SrcRange: zcl.RangeBetween(dot.Range, attrTok.Range),
})
lastRange = attrTok.Range
}
itemExpr := &AnonSymbolExpr{
SrcRange: zcl.RangeBetween(dot.Range, marker.Range),
}
var travExpr Expression
if len(trav) == 0 {
travExpr = itemExpr
} else {
travExpr = &RelativeTraversalExpr{
Source: itemExpr,
Traversal: trav,
SrcRange: zcl.RangeBetween(firstRange, lastRange),
}
}
ret = &SplatExpr{
Source: ret,
Each: travExpr,
Item: itemExpr,
SrcRange: zcl.RangeBetween(dot.Range, lastRange),
MarkerRange: zcl.RangeBetween(dot.Range, marker.Range),
}
default:
diags = append(diags, &zcl.Diagnostic{
@ -523,6 +577,8 @@ Traversal:
// the key value is something constant.
open := p.Read()
// TODO: If we have a TokenStar inside our brackets, parse as
// a Splat expression: foo[*].baz[0].
var close Token
p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets
keyExpr, keyDiags := p.ParseExpression()