From c6b11a8f90760175d9a30486210eeffd30ee6eb2 Mon Sep 17 00:00:00 2001 From: Kyle Brandt Date: Mon, 14 Dec 2020 14:21:16 -0500 Subject: [PATCH] Expressions: support ${my var} syntax (#29819) --- pkg/expr/mathexp/parse/lex.go | 25 +++++++++++++++++++++---- pkg/expr/mathexp/parse/lex_test.go | 13 +++++++++++++ pkg/expr/mathexp/parse/node.go | 2 +- pkg/expr/mathexp/parse/parse.go | 5 +++-- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pkg/expr/mathexp/parse/lex.go b/pkg/expr/mathexp/parse/lex.go index 99ca63e004e..fc48211a741 100644 --- a/pkg/expr/mathexp/parse/lex.go +++ b/pkg/expr/mathexp/parse/lex.go @@ -288,9 +288,29 @@ func lexFunc(l *lexer) stateFn { func lexVar(l *lexer) stateFn { hasChar := false + if l.peek() == '{' { + _ = l.next() + for { + switch r := l.next(); { + case r == '}': + if !hasChar { + return l.errorf("incomplete variable") + } + l.emit(itemVar) + return lexItem + case r == eof: + return l.errorf("unterminated variable missing closing }") + case isVarchar(r) || isSpace(r): + hasChar = true + default: + return l.errorf("unsupported variable character") + } + } + } + for { switch r := l.next(); { - case unicode.IsLetter(r): + case isVarchar(r): hasChar = true // absorb default: @@ -321,9 +341,6 @@ func isSpace(r rune) bool { return unicode.IsSpace(r) } -// isVarchar should maybe be used in place of unicode is letter above, -// but do not want to modify it at this time, so adding lint exception. -// nolint:unused,deadcode func isVarchar(r rune) bool { return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) } diff --git a/pkg/expr/mathexp/parse/lex_test.go b/pkg/expr/mathexp/parse/lex_test.go index 482815e2f98..e5b5ec59fc8 100644 --- a/pkg/expr/mathexp/parse/lex_test.go +++ b/pkg/expr/mathexp/parse/lex_test.go @@ -97,6 +97,16 @@ var lexTests = []lexTest{ {itemNumber, 0, "1.2e-4"}, tEOF, }}, + {"curly brace var", "${My Var}", []item{ + {itemVar, 0, "${My Var}"}, + tEOF, + }}, + {"curly brace var plus 1", "${My Var} + 1", []item{ + {itemVar, 0, "${My Var}"}, + tPlus, + {itemNumber, 0, "1"}, + tEOF, + }}, {"number plus var", "1 + $A", []item{ {itemNumber, 0, "1"}, tPlus, @@ -113,6 +123,9 @@ var lexTests = []lexTest{ {"invalid var", "$", []item{ {itemError, 0, "incomplete variable"}, }}, + {"invalid curly var", "${adf sd", []item{ + {itemError, 0, "unterminated variable missing closing }"}, + }}, } // collect gathers the emitted items into a slice. diff --git a/pkg/expr/mathexp/parse/node.go b/pkg/expr/mathexp/parse/node.go index 79da348580a..711d0bf65e9 100644 --- a/pkg/expr/mathexp/parse/node.go +++ b/pkg/expr/mathexp/parse/node.go @@ -88,7 +88,7 @@ func (t NodeType) String() string { type VarNode struct { NodeType Pos - Name string // Without the $ + Name string // Without the $ or {} Text string // Raw } diff --git a/pkg/expr/mathexp/parse/parse.go b/pkg/expr/mathexp/parse/parse.go index ad2e75c37e1..4652390f29c 100644 --- a/pkg/expr/mathexp/parse/parse.go +++ b/pkg/expr/mathexp/parse/parse.go @@ -300,8 +300,9 @@ func (t *Tree) v() Node { func (t *Tree) Var() (v *VarNode) { token := t.next() varNoPrefix := strings.TrimPrefix(token.val, "$") - t.VarNames = append(t.VarNames, varNoPrefix) - return newVar(token.pos, varNoPrefix, token.val) + varNoBraces := strings.TrimSuffix(strings.TrimPrefix(varNoPrefix, "{"), "}") + t.VarNames = append(t.VarNames, varNoBraces) + return newVar(token.pos, varNoBraces, token.val) } // Func parses a FuncNode.