From bca573d3d0b9dc995879a03f6b50f5067869d105 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Mon, 5 Jun 2017 07:09:04 -0700 Subject: [PATCH] zclsyntax: parsing of the index operator This can either be a traversal or a first-class node depending on whether the given expression is a literal. This exception is made to allow applications to conditionally populate only part of a potentially-large collection if the config is only requesting one or two distinct indices. In particular, it allows the following to be considered a single traversal from the scope: foo.bar[0].baz --- zcl/zclsyntax/expression.go | 25 ++++++++++++++++ zcl/zclsyntax/expression_vars.go | 4 +++ zcl/zclsyntax/parser.go | 49 ++++++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/zcl/zclsyntax/expression.go b/zcl/zclsyntax/expression.go index 2547fb4..03aef60 100644 --- a/zcl/zclsyntax/expression.go +++ b/zcl/zclsyntax/expression.go @@ -387,6 +387,31 @@ func (e *ConditionalExpr) StartRange() zcl.Range { return e.Condition.StartRange() } +type IndexExpr struct { + Collection Expression + Key Expression + + SrcRange zcl.Range + OpenRange zcl.Range +} + +func (e *IndexExpr) walkChildNodes(w internalWalkFunc) { + e.Collection = w(e.Collection).(Expression) + e.Key = w(e.Key).(Expression) +} + +func (e *IndexExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) { + panic("IndexExpr.Value not yet implemented") +} + +func (e *IndexExpr) Range() zcl.Range { + return e.SrcRange +} + +func (e *IndexExpr) StartRange() zcl.Range { + return e.OpenRange +} + type TupleConsExpr struct { Exprs []Expression diff --git a/zcl/zclsyntax/expression_vars.go b/zcl/zclsyntax/expression_vars.go index 014401e..5b84bad 100755 --- a/zcl/zclsyntax/expression_vars.go +++ b/zcl/zclsyntax/expression_vars.go @@ -19,6 +19,10 @@ func (e *FunctionCallExpr) Variables() []zcl.Traversal { return Variables(e) } +func (e *IndexExpr) Variables() []zcl.Traversal { + return Variables(e) +} + func (e *LiteralValueExpr) Variables() []zcl.Traversal { return Variables(e) } diff --git a/zcl/zclsyntax/parser.go b/zcl/zclsyntax/parser.go index 1cb17e6..f3837f2 100644 --- a/zcl/zclsyntax/parser.go +++ b/zcl/zclsyntax/parser.go @@ -461,10 +461,11 @@ Traversal: case TokenDot: // Attribute access or splat dot := p.Read() - attrTok := p.Read() + attrTok := p.Peek() switch attrTok.Type { case TokenIdent: + attrTok = p.Read() // eat token name := string(attrTok.Bytes) rng := zcl.RangeBetween(dot.Range, attrTok.Range) step := zcl.TraverseAttr{ @@ -491,7 +492,51 @@ Traversal: // in order to support autocomplete triggered by typing a // period. p.setRecovery() - break Traversal + } + + case TokenOBrack: + // Indexing of a collection. + // This may or may not be a zcl.Traverser, depending on whether + // the key value is something constant. + + open := p.Read() + 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, &zcl.Diagnostic{ + Severity: zcl.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.PushIncludeNewlines(true) + + if lit, isLit := keyExpr.(*LiteralValueExpr); isLit { + litKey, _ := lit.Value(nil) + rng := zcl.RangeBetween(open.Range, close.Range) + step := &zcl.TraverseIndex{ + Key: litKey, + SrcRange: rng, + } + ret = makeRelativeTraversal(ret, step, rng) + } else { + rng := zcl.RangeBetween(open.Range, close.Range) + ret = &IndexExpr{ + Collection: ret, + Key: keyExpr, + + SrcRange: rng, + OpenRange: open.Range, + } } default: