diff --git a/promql/lex.go b/promql/lex.go index 189d58ac2..552ed6917 100644 --- a/promql/lex.go +++ b/promql/lex.go @@ -666,7 +666,8 @@ func lexNumberOrDuration(l *lexer) stateFn { // not necessarily a valid number. This case is caught by the parser. func (l *lexer) scanNumber() bool { digits := "0123456789" - if l.accept("0") && l.accept("xX") { + // Disallow hexal in series descriptions as the syntax is ambiguous. + if !l.seriesDesc && l.accept("0") && l.accept("xX") { digits = "0123456789abcdefABCDEF" } l.acceptRun(digits) @@ -677,11 +678,12 @@ func (l *lexer) scanNumber() bool { l.accept("+-") l.acceptRun("0123456789") } - // Next thing must not be alphanumeric. - if isAlphaNumeric(l.peek()) && !l.seriesDesc { - return false + // Next thing must not be alphanumeric unless it's the times token + // for series repetitions. + if r := l.peek(); (l.seriesDesc && r == 'x') || !isAlphaNumeric(r) { + return true } - return true + return false } // lexIdentifier scans an alphanumeric identifier. The next character diff --git a/promql/parse_test.go b/promql/parse_test.go index dfeac7eec..c50b8413c 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -1274,6 +1274,13 @@ var testSeries = []struct { "a": "b", }, expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37), + }, { + input: `my_metric{a="b"} 1 2 3-0x4`, + expectedMetric: clientmodel.Metric{ + clientmodel.MetricNameLabel: "my_metric", + "a": "b", + }, + expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3), }, { input: `my_metric{a="b"} 1 3 _ 5 _x4`, expectedMetric: clientmodel.Metric{