From fdd68833f3a86a137e387c3728223815e3ed9e3c Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sun, 18 Jun 2017 07:44:57 -0700 Subject: [PATCH] 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. --- zcl/zclsyntax/expression_template.go | 37 ++++++++++++++++++++-------- zcl/zclsyntax/expression_vars.go | 4 +++ zcl/zclsyntax/parser.go | 16 +++++++++--- zcl/zclsyntax/parser_template.go | 25 ++++++++++++------- zcl/zclsyntax/parser_test.go | 1 - 5 files changed, 59 insertions(+), 24 deletions(-) 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},