From 408574a6e1e053f5744124294447680491116485 Mon Sep 17 00:00:00 2001 From: Tobias Guggenmos Date: Wed, 27 Nov 2019 12:59:03 +0000 Subject: [PATCH] promql: Allow injecting fake tokens into the generated parser (#6381) * promql: Allow injecting fake tokens into the generated parser Yacc grammars do not support having multiple start symbols. To work around that restriction, it is possible to inject fake tokens into the lexer stream, as described here https://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html . This is part of the parser rewrite effort described in #6256. Signed-off-by: Tobias Guggenmos --- promql/lex.go | 4 ++++ promql/parse.go | 30 +++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/promql/lex.go b/promql/lex.go index ddb2b920c..4941ee1b0 100644 --- a/promql/lex.go +++ b/promql/lex.go @@ -193,6 +193,10 @@ const ( GROUP_RIGHT BOOL keywordsEnd + + startSymbolsStart + // Start symbols for the generated parser. + startSymbolsEnd ) var key = map[string]ItemType{ diff --git a/promql/parse.go b/promql/parse.go index 4d4e2096e..aad5815a4 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -35,6 +35,9 @@ type parser struct { lex *lexer token item peeking bool + + inject item + injecting bool } // ParseErr wraps a parsing error with line and position context. @@ -352,7 +355,12 @@ type yySymType item // // For more information, see https://godoc.org/golang.org/x/tools/cmd/goyacc. func (p *parser) Lex(lval *yySymType) int { - *lval = yySymType(p.next()) + if p.injecting { + *lval = yySymType(p.inject) + p.injecting = false + } else { + *lval = yySymType(p.next()) + } return int(item(*lval).typ) } @@ -364,6 +372,26 @@ func (p *parser) Error(e string) { p.errorf(e) } +// InjectItem allows injecting a single item at the beginning of the token stream +// consumed by the generated parser. +// This allows having multiple start symbols as described in +// https://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html . +// Only the Lex function used by the generated parser is affected by this injected item. +// Trying to inject when a previously injected item has not yet been consumed will panic. +// Only item types that are supposed to be used as start symbols are allowed as an argument. +func (p *parser) InjectItem(typ ItemType) { + if p.injecting { + panic("cannot inject multiple items into the token stream") + } + + if typ <= startSymbolsStart || typ >= startSymbolsEnd { + panic("cannot inject symbol that isn't start symbol") + } + + p.inject = item{typ: typ} + p.injecting = true +} + // expr parses any expression. func (p *parser) expr() Expr { // Parse the starting expression.