zclsyntax: parsing and evaluation of tuple constructors

This commit is contained in:
Martin Atkins 2017-06-04 14:22:51 -07:00
parent 4488df0cd8
commit c3f4694e06
4 changed files with 144 additions and 3 deletions

View File

@ -362,3 +362,38 @@ func (e *ConditionalExpr) Range() zcl.Range {
func (e *ConditionalExpr) StartRange() zcl.Range {
return e.Condition.StartRange()
}
type TupleConsExpr struct {
Exprs []Expression
SrcRange zcl.Range
OpenRange zcl.Range
}
func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) {
for i, expr := range e.Exprs {
e.Exprs[i] = w(expr).(Expression)
}
}
func (e *TupleConsExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
var vals []cty.Value
var diags zcl.Diagnostics
vals = make([]cty.Value, len(e.Exprs))
for i, expr := range e.Exprs {
val, valDiags := expr.Value(ctx)
vals[i] = val
diags = append(diags, valDiags...)
}
return cty.TupleVal(vals), diags
}
func (e *TupleConsExpr) Range() zcl.Range {
return e.SrcRange
}
func (e *TupleConsExpr) StartRange() zcl.Range {
return e.OpenRange
}

View File

@ -146,6 +146,39 @@ upper(
cty.StringVal("FOO"),
0,
},
{
`[]`,
nil,
cty.EmptyTupleVal,
0,
},
{
`[1]`,
nil,
cty.TupleVal([]cty.Value{cty.NumberIntVal(1)}),
0,
},
{
`[1,]`,
nil,
cty.TupleVal([]cty.Value{cty.NumberIntVal(1)}),
0,
},
{
`[1,true]`,
nil,
cty.TupleVal([]cty.Value{cty.NumberIntVal(1), cty.True}),
0,
},
{
`[
1,
true
]`,
nil,
cty.TupleVal([]cty.Value{cty.NumberIntVal(1), cty.True}),
0,
},
}
for _, test := range tests {

View File

@ -31,6 +31,10 @@ func (e *TemplateExpr) Variables() []zcl.Traversal {
return Variables(e)
}
func (e *TupleConsExpr) Variables() []zcl.Traversal {
return Variables(e)
}
func (e *UnaryOpExpr) Variables() []zcl.Traversal {
return Variables(e)
}

View File

@ -587,6 +587,9 @@ func (p *parser) parseExpressionTerm() (Expression, zcl.Diagnostics) {
SymbolRange: tok.Range,
}, diags
case TokenOBrack:
return p.parseTupleCons()
default:
var diags zcl.Diagnostics
if !p.recovery {
@ -683,6 +686,72 @@ Token:
}, diags
}
func (p *parser) parseTupleCons() (Expression, zcl.Diagnostics) {
open := p.Read()
if open.Type != TokenOBrack {
// Should never happen if callers are behaving
panic("parseTupleCons called without peeker pointing to open bracket")
}
var close Token
p.PushIncludeNewlines(false)
defer p.PopIncludeNewlines()
var diags zcl.Diagnostics
var exprs []Expression
for {
next := p.Peek()
if next.Type == TokenCBrack {
close = p.Read() // eat closer
break
}
expr, exprDiags := p.ParseExpression()
exprs = append(exprs, expr)
diags = append(diags, exprDiags...)
if p.recovery && exprDiags.HasErrors() {
// If expression parsing failed then we are probably in a strange
// place in the token stream, so we'll bail out and try to reset
// to after our closing bracket to allow parsing to continue.
close = p.recover(TokenCBrack)
break
}
next = p.Peek()
if next.Type == TokenCBrack {
close = p.Read() // eat closer
break
}
if next.Type != TokenComma {
if !p.recovery {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Missing item separator",
Detail: "Expected a comma to mark the beginning of the next item.",
Subject: &next.Range,
Context: zcl.RangeBetween(open.Range, next.Range).Ptr(),
})
}
close = p.recover(TokenCBrack)
break
}
p.Read() // eat comma
}
return &TupleConsExpr{
Exprs: exprs,
SrcRange: zcl.RangeBetween(open.Range, close.Range),
OpenRange: open.Range,
}, diags
}
func (p *parser) ParseTemplate(end TokenType) (Expression, zcl.Diagnostics) {
var parts []Expression
var diags zcl.Diagnostics
@ -1061,7 +1130,7 @@ func (p *parser) setRecovery() {
// the end of the _current_ instance of that bracketer, skipping over any
// nested instances. This is a best-effort operation and may have
// unpredictable results on input with bad bracketer nesting.
func (p *parser) recover(end TokenType) {
func (p *parser) recover(end TokenType) Token {
start := p.oppositeBracket(end)
p.recovery = true
@ -1082,12 +1151,12 @@ func (p *parser) recover(end TokenType) {
nest++
case end:
if nest < 1 {
return
return tok
}
nest--
case TokenEOF:
return
return tok
}
}
}