Fix parsing of label names which are also keywords

The current separation between lexer and parser is a bit fuzzy when it
comes to operators, aggregators and other keywords. The lexer already
tries to determine the type of a token, even though that type might
change depending on the context.

This led to the problematic behavior that no tokens known to the lexer
could be used as label names, including operators (and, by, ...),
aggregators (count, quantile, ...) or other keywords (for, offset, ...).

This change additionally checks whether an identifier is one of these
types. We might want to check whether the specific item identification
should be moved from the lexer to the parser.
This commit is contained in:
Tobias Schmidt 2016-09-07 15:16:34 -04:00
parent 24db241bd5
commit 04ae6196f2
3 changed files with 35 additions and 1 deletions

View File

@ -889,3 +889,16 @@ func isDigit(r rune) bool {
func isAlpha(r rune) bool {
return r == '_' || ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z')
}
// isLabel reports whether the string can be used as label.
func isLabel(s string) bool {
if len(s) == 0 || !isAlpha(rune(s[0])) {
return false
}
for _, c := range s[1:] {
if !isAlphaNumeric(c) {
return false
}
}
return true
}

View File

@ -673,7 +673,10 @@ func (p *parser) labels() model.LabelNames {
labels := model.LabelNames{}
if p.peek().typ != itemRightParen {
for {
id := p.expect(itemIdentifier, ctx)
id := p.next()
if !isLabel(id.val) {
p.errorf("unexpected %s in %s, expected label", id.desc(), ctx)
}
labels = append(labels, model.LabelName(id.val))
if p.peek().typ != itemComma {

View File

@ -1225,6 +1225,24 @@ var testExpr = []struct {
},
Param: &StringLiteral{"value"},
},
}, {
// Test usage of keywords as label names.
input: "sum without(and, by, avg, count, alert, annotations)(some_metric)",
expected: &AggregateExpr{
Op: itemSum,
Without: true,
Expr: &VectorSelector{
Name: "some_metric",
LabelMatchers: metric.LabelMatchers{
mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"),
},
},
Grouping: model.LabelNames{"and", "by", "avg", "count", "alert", "annotations"},
},
}, {
input: "sum without(==)(some_metric)",
fail: true,
errMsg: "unexpected <op:==> in grouping opts, expected label",
}, {
input: `sum some_metric by (test)`,
fail: true,