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 <tguggenm@redhat.com>
This commit is contained in:
Tobias Guggenmos 2019-11-27 12:59:03 +00:00 committed by Brian Brazil
parent fa1489e35c
commit 408574a6e1
2 changed files with 33 additions and 1 deletions

View File

@ -193,6 +193,10 @@ const (
GROUP_RIGHT
BOOL
keywordsEnd
startSymbolsStart
// Start symbols for the generated parser.
startSymbolsEnd
)
var key = map[string]ItemType{

View File

@ -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.