diff --git a/zcl/zclsyntax/expression_template.go b/zcl/zclsyntax/expression_template.go index 5823331..4c50e15 100644 --- a/zcl/zclsyntax/expression_template.go +++ b/zcl/zclsyntax/expression_template.go @@ -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 +} diff --git a/zcl/zclsyntax/expression_vars.go b/zcl/zclsyntax/expression_vars.go index 267f95d..9d0c411 100755 --- a/zcl/zclsyntax/expression_vars.go +++ b/zcl/zclsyntax/expression_vars.go @@ -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) } diff --git a/zcl/zclsyntax/parser.go b/zcl/zclsyntax/parser.go index 8e25da7..d5d880a 100644 --- a/zcl/zclsyntax/parser.go +++ b/zcl/zclsyntax/parser.go @@ -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 diff --git a/zcl/zclsyntax/parser_template.go b/zcl/zclsyntax/parser_template.go index c115d38..7c0114a 100644 --- a/zcl/zclsyntax/parser_template.go +++ b/zcl/zclsyntax/parser_template.go @@ -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 diff --git a/zcl/zclsyntax/parser_test.go b/zcl/zclsyntax/parser_test.go index 5fc00c6..9448bb6 100644 --- a/zcl/zclsyntax/parser_test.go +++ b/zcl/zclsyntax/parser_test.go @@ -433,7 +433,6 @@ block "valid" {} }, }, }, - Unwrap: false, SrcRange: zcl.Range{ Start: zcl.Pos{Line: 1, Column: 5, Byte: 4},