hcl/hclsyntax: Parsing of the "full splat" operator
The evaluation of this was there but the parsing was still a TODO comment from early development. Whoops! Fortunately the existing parser functionality makes this straightforward since we can just have the traversal parser recursively call itself. This fixes #63.
This commit is contained in:
parent
ebe27107e1
commit
b056768a1c
@ -781,6 +781,14 @@ upper(
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`{name: "Steve"}[*].name`,
|
||||
nil,
|
||||
cty.TupleVal([]cty.Value{
|
||||
cty.StringVal("Steve"),
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`set.*.name`,
|
||||
&hcl.EvalContext{
|
||||
@ -915,6 +923,31 @@ upper(
|
||||
cty.StringVal("Steve"),
|
||||
0,
|
||||
},
|
||||
{
|
||||
// For a "full" splat, an index operator is consumed as part
|
||||
// of the splat's traversal.
|
||||
`[{names: ["Steve"]}, {names: ["Ermintrude"]}][*].names[0]`,
|
||||
nil,
|
||||
cty.TupleVal([]cty.Value{cty.StringVal("Steve"), cty.StringVal("Ermintrude")}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Another "full" splat, this time with the index first.
|
||||
`[[{name: "Steve"}], [{name: "Ermintrude"}]][*][0].name`,
|
||||
nil,
|
||||
cty.TupleVal([]cty.Value{cty.StringVal("Steve"), cty.StringVal("Ermintrude")}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
// Full splats can nest, which produces nested tuples.
|
||||
`[[{name: "Steve"}], [{name: "Ermintrude"}]][*][*].name`,
|
||||
nil,
|
||||
cty.TupleVal([]cty.Value{
|
||||
cty.TupleVal([]cty.Value{cty.StringVal("Steve")}),
|
||||
cty.TupleVal([]cty.Value{cty.StringVal("Ermintrude")}),
|
||||
}),
|
||||
0,
|
||||
},
|
||||
{
|
||||
`[["hello"], ["goodbye"]].*.*`,
|
||||
nil,
|
||||
|
@ -462,7 +462,14 @@ func (p *parser) parseBinaryOps(ops []map[TokenType]*Operation) (Expression, hcl
|
||||
|
||||
func (p *parser) parseExpressionWithTraversals() (Expression, hcl.Diagnostics) {
|
||||
term, diags := p.parseExpressionTerm()
|
||||
ret := term
|
||||
ret, moreDiags := p.parseExpressionTraversals(term)
|
||||
diags = append(diags, moreDiags...)
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
func (p *parser) parseExpressionTraversals(from Expression) (Expression, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
ret := from
|
||||
|
||||
Traversal:
|
||||
for {
|
||||
@ -660,44 +667,81 @@ 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()
|
||||
diags = append(diags, keyDiags...)
|
||||
if p.recovery && keyDiags.HasErrors() {
|
||||
close = p.recover(TokenCBrack)
|
||||
} else {
|
||||
close = p.Read()
|
||||
switch p.Peek().Type {
|
||||
case TokenStar:
|
||||
// This is a full splat expression, like foo[*], which consumes
|
||||
// the rest of the traversal steps after it using a recursive
|
||||
// call to this function.
|
||||
p.Read() // consume star
|
||||
close := p.Read()
|
||||
if close.Type != TokenCBrack && !p.recovery {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Missing close bracket on index",
|
||||
Detail: "The index operator must end with a closing bracket (\"]\").",
|
||||
Summary: "Missing close bracket on splat index",
|
||||
Detail: "The star for a full splat operator must be immediately followed by a closing bracket (\"]\").",
|
||||
Subject: &close.Range,
|
||||
})
|
||||
close = p.recover(TokenCBrack)
|
||||
}
|
||||
}
|
||||
p.PopIncludeNewlines()
|
||||
|
||||
if lit, isLit := keyExpr.(*LiteralValueExpr); isLit {
|
||||
litKey, _ := lit.Value(nil)
|
||||
rng := hcl.RangeBetween(open.Range, close.Range)
|
||||
step := hcl.TraverseIndex{
|
||||
Key: litKey,
|
||||
SrcRange: rng,
|
||||
// Splat expressions use a special "anonymous symbol" as a
|
||||
// placeholder in an expression to be evaluated once for each
|
||||
// item in the source expression.
|
||||
itemExpr := &AnonSymbolExpr{
|
||||
SrcRange: hcl.RangeBetween(open.Range, close.Range),
|
||||
}
|
||||
ret = makeRelativeTraversal(ret, step, rng)
|
||||
} else {
|
||||
rng := hcl.RangeBetween(open.Range, close.Range)
|
||||
ret = &IndexExpr{
|
||||
Collection: ret,
|
||||
Key: keyExpr,
|
||||
// Now we'll recursively call this same function to eat any
|
||||
// remaining traversal steps against the anonymous symbol.
|
||||
travExpr, nestedDiags := p.parseExpressionTraversals(itemExpr)
|
||||
diags = append(diags, nestedDiags...)
|
||||
|
||||
SrcRange: rng,
|
||||
OpenRange: open.Range,
|
||||
ret = &SplatExpr{
|
||||
Source: ret,
|
||||
Each: travExpr,
|
||||
Item: itemExpr,
|
||||
|
||||
SrcRange: hcl.RangeBetween(open.Range, travExpr.Range()),
|
||||
MarkerRange: hcl.RangeBetween(open.Range, close.Range),
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
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, &hcl.Diagnostic{
|
||||
Severity: hcl.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.PopIncludeNewlines()
|
||||
|
||||
if lit, isLit := keyExpr.(*LiteralValueExpr); isLit {
|
||||
litKey, _ := lit.Value(nil)
|
||||
rng := hcl.RangeBetween(open.Range, close.Range)
|
||||
step := hcl.TraverseIndex{
|
||||
Key: litKey,
|
||||
SrcRange: rng,
|
||||
}
|
||||
ret = makeRelativeTraversal(ret, step, rng)
|
||||
} else {
|
||||
rng := hcl.RangeBetween(open.Range, close.Range)
|
||||
ret = &IndexExpr{
|
||||
Collection: ret,
|
||||
Key: keyExpr,
|
||||
|
||||
SrcRange: rng,
|
||||
OpenRange: open.Range,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user