zclsyntax: template single-interpolation case as separate AST node
Rather than setting an "Unwrap" field on the existing TemplateExpr, we'll instead use a separate, simpler AST node. There is very little in common between these two cases, so overloading the TemplateExpr node doesn't buy much. A separate node is needed here, rather than just returning the wrapped node directly, to give us somewhere to capture the full source range extent of the wrapping template, whereas the inner expression only captures the range of itself. This is important both for good diagnostics and for transforming zclsyntax AST into zclwrite AST.
This commit is contained in:
parent
e594a232b3
commit
fdd68833f3
@ -10,8 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type TemplateExpr struct {
|
||||
Parts []Expression
|
||||
Unwrap bool
|
||||
Parts []Expression
|
||||
|
||||
SrcRange zcl.Range
|
||||
}
|
||||
@ -23,14 +22,6 @@ func (e *TemplateExpr) walkChildNodes(w internalWalkFunc) {
|
||||
}
|
||||
|
||||
func (e *TemplateExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
if e.Unwrap {
|
||||
if len(e.Parts) != 1 {
|
||||
// should never happen - parser bug, if so
|
||||
panic("Unwrap set with len(e.Parts) != 1")
|
||||
}
|
||||
return e.Parts[0].Value(ctx)
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
var diags zcl.Diagnostics
|
||||
isKnown := true
|
||||
@ -93,3 +84,29 @@ func (e *TemplateExpr) Range() zcl.Range {
|
||||
func (e *TemplateExpr) StartRange() zcl.Range {
|
||||
return e.Parts[0].StartRange()
|
||||
}
|
||||
|
||||
// TemplateWrapExpr is used instead of a TemplateExpr when a template
|
||||
// consists _only_ of a single interpolation sequence. In that case, the
|
||||
// template's result is the single interpolation's result, verbatim with
|
||||
// no type conversions.
|
||||
type TemplateWrapExpr struct {
|
||||
Wrapped Expression
|
||||
|
||||
SrcRange zcl.Range
|
||||
}
|
||||
|
||||
func (e *TemplateWrapExpr) walkChildNodes(w internalWalkFunc) {
|
||||
e.Wrapped = w(e.Wrapped).(Expression)
|
||||
}
|
||||
|
||||
func (e *TemplateWrapExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
||||
return e.Wrapped.Value(ctx)
|
||||
}
|
||||
|
||||
func (e *TemplateWrapExpr) Range() zcl.Range {
|
||||
return e.SrcRange
|
||||
}
|
||||
|
||||
func (e *TemplateWrapExpr) StartRange() zcl.Range {
|
||||
return e.SrcRange
|
||||
}
|
||||
|
@ -55,6 +55,10 @@ func (e *TemplateExpr) Variables() []zcl.Traversal {
|
||||
return Variables(e)
|
||||
}
|
||||
|
||||
func (e *TemplateWrapExpr) Variables() []zcl.Traversal {
|
||||
return Variables(e)
|
||||
}
|
||||
|
||||
func (e *TupleConsExpr) Variables() []zcl.Traversal {
|
||||
return Variables(e)
|
||||
}
|
||||
|
@ -761,14 +761,22 @@ func (p *parser) parseExpressionTerm() (Expression, zcl.Diagnostics) {
|
||||
case TokenOQuote, TokenOHeredoc:
|
||||
open := p.Read() // eat opening marker
|
||||
closer := p.oppositeBracket(open.Type)
|
||||
exprs, unwrap, _, diags := p.parseTemplateInner(closer)
|
||||
exprs, passthru, _, diags := p.parseTemplateInner(closer)
|
||||
|
||||
closeRange := p.PrevRange()
|
||||
|
||||
return &TemplateExpr{
|
||||
Parts: exprs,
|
||||
Unwrap: unwrap,
|
||||
if passthru {
|
||||
if len(exprs) != 1 {
|
||||
panic("passthru set with len(exprs) != 1")
|
||||
}
|
||||
return &TemplateWrapExpr{
|
||||
Wrapped: exprs[0],
|
||||
SrcRange: zcl.RangeBetween(open.Range, closeRange),
|
||||
}, diags
|
||||
}
|
||||
|
||||
return &TemplateExpr{
|
||||
Parts: exprs,
|
||||
SrcRange: zcl.RangeBetween(open.Range, closeRange),
|
||||
}, diags
|
||||
|
||||
|
@ -14,12 +14,20 @@ func (p *parser) ParseTemplate() (Expression, zcl.Diagnostics) {
|
||||
}
|
||||
|
||||
func (p *parser) parseTemplate(end TokenType) (Expression, zcl.Diagnostics) {
|
||||
exprs, unwrap, rng, diags := p.parseTemplateInner(end)
|
||||
exprs, passthru, rng, diags := p.parseTemplateInner(end)
|
||||
|
||||
if passthru {
|
||||
if len(exprs) != 1 {
|
||||
panic("passthru set with len(exprs) != 1")
|
||||
}
|
||||
return &TemplateWrapExpr{
|
||||
Wrapped: exprs[0],
|
||||
SrcRange: rng,
|
||||
}, diags
|
||||
}
|
||||
|
||||
return &TemplateExpr{
|
||||
Parts: exprs,
|
||||
Unwrap: unwrap,
|
||||
|
||||
Parts: exprs,
|
||||
SrcRange: rng,
|
||||
}, diags
|
||||
}
|
||||
@ -33,14 +41,14 @@ func (p *parser) parseTemplateInner(end TokenType) ([]Expression, bool, zcl.Rang
|
||||
exprs, exprsDiags := tp.parseRoot()
|
||||
diags = append(diags, exprsDiags...)
|
||||
|
||||
unwrap := false
|
||||
passthru := false
|
||||
if len(parts.Tokens) == 2 { // one real token and one synthetic "end" token
|
||||
if _, isInterp := parts.Tokens[0].(*templateInterpToken); isInterp {
|
||||
unwrap = true
|
||||
passthru = true
|
||||
}
|
||||
}
|
||||
|
||||
return exprs, unwrap, parts.SrcRange, diags
|
||||
return exprs, passthru, parts.SrcRange, diags
|
||||
}
|
||||
|
||||
type templateParser struct {
|
||||
@ -87,8 +95,7 @@ func (p *templateParser) parseExpr() (Expression, zcl.Diagnostics) {
|
||||
return p.parseIf()
|
||||
|
||||
case *templateForToken:
|
||||
// TODO: implement
|
||||
panic("template for token not yet implemented")
|
||||
return p.parseFor()
|
||||
|
||||
case *templateEndToken:
|
||||
p.Read() // eat erroneous token
|
||||
|
@ -433,7 +433,6 @@ block "valid" {}
|
||||
},
|
||||
},
|
||||
},
|
||||
Unwrap: false,
|
||||
|
||||
SrcRange: zcl.Range{
|
||||
Start: zcl.Pos{Line: 1, Column: 5, Byte: 4},
|
||||
|
Loading…
Reference in New Issue
Block a user