mirror of
https://github.com/prometheus/prometheus
synced 2024-12-23 15:04:13 +00:00
88675710f9
Previously, the api was evaluating this regex to determine if the label
name was valid or not:
14bac55a99/model/labels.go (L94)
However, I believe that the `IsValid()` function is what ought to be
used in the brave new utf8 era.
**Before**
```
$ curl localhost:9090/api/v1/label/host.name/values
{"status":"error","errorType":"bad_data","error":"invalid label name: \"host.name\""}
```
**After**
```
$ curl localhost:9090/api/v1/label/host.name/values
{"status":"success","data":["localhost"]}
```
It's very likely that I'm missing something here or you were already
planning to do this at some point but I just encountered this issue and
figured I'd give it a go.
Signed-off-by: Owen Williams <owen.williams@grafana.com>
1000 lines
35 KiB
Plaintext
1000 lines
35 KiB
Plaintext
// Copyright 2019 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
%{
|
|
package parser
|
|
|
|
import (
|
|
"math"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
"github.com/prometheus/prometheus/model/value"
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
"github.com/prometheus/prometheus/promql/parser/posrange"
|
|
|
|
"github.com/prometheus/common/model"
|
|
)
|
|
|
|
%}
|
|
|
|
%union {
|
|
node Node
|
|
item Item
|
|
matchers []*labels.Matcher
|
|
matcher *labels.Matcher
|
|
label labels.Label
|
|
labels labels.Labels
|
|
lblList []labels.Label
|
|
strings []string
|
|
series []SequenceValue
|
|
histogram *histogram.FloatHistogram
|
|
descriptors map[string]interface{}
|
|
bucket_set []float64
|
|
int int64
|
|
uint uint64
|
|
float float64
|
|
}
|
|
|
|
|
|
%token <item>
|
|
EQL
|
|
BLANK
|
|
COLON
|
|
COMMA
|
|
COMMENT
|
|
DURATION
|
|
EOF
|
|
ERROR
|
|
IDENTIFIER
|
|
LEFT_BRACE
|
|
LEFT_BRACKET
|
|
LEFT_PAREN
|
|
OPEN_HIST
|
|
CLOSE_HIST
|
|
METRIC_IDENTIFIER
|
|
NUMBER
|
|
RIGHT_BRACE
|
|
RIGHT_BRACKET
|
|
RIGHT_PAREN
|
|
SEMICOLON
|
|
SPACE
|
|
STRING
|
|
TIMES
|
|
|
|
// Histogram Descriptors.
|
|
%token histogramDescStart
|
|
%token <item>
|
|
SUM_DESC
|
|
COUNT_DESC
|
|
SCHEMA_DESC
|
|
OFFSET_DESC
|
|
NEGATIVE_OFFSET_DESC
|
|
BUCKETS_DESC
|
|
NEGATIVE_BUCKETS_DESC
|
|
ZERO_BUCKET_DESC
|
|
ZERO_BUCKET_WIDTH_DESC
|
|
CUSTOM_VALUES_DESC
|
|
COUNTER_RESET_HINT_DESC
|
|
%token histogramDescEnd
|
|
|
|
// Operators.
|
|
%token operatorsStart
|
|
%token <item>
|
|
ADD
|
|
DIV
|
|
EQLC
|
|
EQL_REGEX
|
|
GTE
|
|
GTR
|
|
LAND
|
|
LOR
|
|
LSS
|
|
LTE
|
|
LUNLESS
|
|
MOD
|
|
MUL
|
|
NEQ
|
|
NEQ_REGEX
|
|
POW
|
|
SUB
|
|
AT
|
|
ATAN2
|
|
%token operatorsEnd
|
|
|
|
// Aggregators.
|
|
%token aggregatorsStart
|
|
%token <item>
|
|
AVG
|
|
BOTTOMK
|
|
COUNT
|
|
COUNT_VALUES
|
|
GROUP
|
|
MAX
|
|
MIN
|
|
QUANTILE
|
|
STDDEV
|
|
STDVAR
|
|
SUM
|
|
TOPK
|
|
LIMITK
|
|
LIMIT_RATIO
|
|
%token aggregatorsEnd
|
|
|
|
// Keywords.
|
|
%token keywordsStart
|
|
%token <item>
|
|
BOOL
|
|
BY
|
|
GROUP_LEFT
|
|
GROUP_RIGHT
|
|
IGNORING
|
|
OFFSET
|
|
ON
|
|
WITHOUT
|
|
%token keywordsEnd
|
|
|
|
// Preprocessors.
|
|
%token preprocessorStart
|
|
%token <item>
|
|
START
|
|
END
|
|
%token preprocessorEnd
|
|
|
|
// Counter reset hints.
|
|
%token counterResetHintsStart
|
|
%token <item>
|
|
UNKNOWN_COUNTER_RESET
|
|
COUNTER_RESET
|
|
NOT_COUNTER_RESET
|
|
GAUGE_TYPE
|
|
%token counterResetHintsEnd
|
|
|
|
// Start symbols for the generated parser.
|
|
%token startSymbolsStart
|
|
%token
|
|
START_METRIC
|
|
START_SERIES_DESCRIPTION
|
|
START_EXPRESSION
|
|
START_METRIC_SELECTOR
|
|
%token startSymbolsEnd
|
|
|
|
|
|
// Type definitions for grammar rules.
|
|
%type <matchers> label_match_list
|
|
%type <matcher> label_matcher
|
|
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint
|
|
%type <labels> label_set metric
|
|
%type <lblList> label_set_list
|
|
%type <label> label_set_item
|
|
%type <strings> grouping_label_list grouping_labels maybe_grouping_labels
|
|
%type <series> series_item series_values
|
|
%type <histogram> histogram_series_value
|
|
%type <descriptors> histogram_desc_map histogram_desc_item
|
|
%type <bucket_set> bucket_set bucket_set_list
|
|
%type <int> int
|
|
%type <uint> uint
|
|
%type <float> number series_value signed_number signed_or_unsigned_number
|
|
%type <node> step_invariant_expr aggregate_expr aggregate_modifier bin_modifier binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers label_matchers matrix_selector number_duration_literal offset_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector
|
|
|
|
%start start
|
|
|
|
// Operators are listed with increasing precedence.
|
|
%left LOR
|
|
%left LAND LUNLESS
|
|
%left EQLC GTE GTR LSS LTE NEQ
|
|
%left ADD SUB
|
|
%left MUL DIV MOD ATAN2
|
|
%right POW
|
|
|
|
// Offset modifiers do not have associativity.
|
|
%nonassoc OFFSET
|
|
|
|
// This ensures that it is always attempted to parse range or subquery selectors when a left
|
|
// bracket is encountered.
|
|
%right LEFT_BRACKET
|
|
|
|
%%
|
|
|
|
start :
|
|
START_METRIC metric
|
|
{ yylex.(*parser).generatedParserResult = $2 }
|
|
| START_SERIES_DESCRIPTION series_description
|
|
| START_EXPRESSION /* empty */ EOF
|
|
{ yylex.(*parser).addParseErrf(posrange.PositionRange{}, "no expression found in input")}
|
|
| START_EXPRESSION expr
|
|
{ yylex.(*parser).generatedParserResult = $2 }
|
|
| START_METRIC_SELECTOR vector_selector
|
|
{ yylex.(*parser).generatedParserResult = $2 }
|
|
| start EOF
|
|
| error /* If none of the more detailed error messages are triggered, we fall back to this. */
|
|
{ yylex.(*parser).unexpected("","") }
|
|
;
|
|
|
|
expr :
|
|
aggregate_expr
|
|
| binary_expr
|
|
| function_call
|
|
| matrix_selector
|
|
| number_duration_literal
|
|
| offset_expr
|
|
| paren_expr
|
|
| string_literal
|
|
| subquery_expr
|
|
| unary_expr
|
|
| vector_selector
|
|
| step_invariant_expr
|
|
;
|
|
|
|
/*
|
|
* Aggregations.
|
|
*/
|
|
|
|
aggregate_expr : aggregate_op aggregate_modifier function_call_body
|
|
{ $$ = yylex.(*parser).newAggregateExpr($1, $2, $3) }
|
|
| aggregate_op function_call_body aggregate_modifier
|
|
{ $$ = yylex.(*parser).newAggregateExpr($1, $3, $2) }
|
|
| aggregate_op function_call_body
|
|
{ $$ = yylex.(*parser).newAggregateExpr($1, &AggregateExpr{}, $2) }
|
|
| aggregate_op error
|
|
{
|
|
yylex.(*parser).unexpected("aggregation","");
|
|
$$ = yylex.(*parser).newAggregateExpr($1, &AggregateExpr{}, Expressions{})
|
|
}
|
|
;
|
|
|
|
aggregate_modifier:
|
|
BY grouping_labels
|
|
{
|
|
$$ = &AggregateExpr{
|
|
Grouping: $2,
|
|
}
|
|
}
|
|
| WITHOUT grouping_labels
|
|
{
|
|
$$ = &AggregateExpr{
|
|
Grouping: $2,
|
|
Without: true,
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Binary expressions.
|
|
*/
|
|
|
|
// Operator precedence only works if each of those is listed separately.
|
|
binary_expr : expr ADD bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr ATAN2 bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr DIV bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr EQLC bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr GTE bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr GTR bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr LAND bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr LOR bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr LSS bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr LTE bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr LUNLESS bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr MOD bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr MUL bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr NEQ bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr POW bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
| expr SUB bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
|
;
|
|
|
|
// Using left recursion for the modifier rules, helps to keep the parser stack small and
|
|
// reduces allocations.
|
|
bin_modifier : group_modifiers;
|
|
|
|
bool_modifier : /* empty */
|
|
{ $$ = &BinaryExpr{
|
|
VectorMatching: &VectorMatching{Card: CardOneToOne},
|
|
}
|
|
}
|
|
| BOOL
|
|
{ $$ = &BinaryExpr{
|
|
VectorMatching: &VectorMatching{Card: CardOneToOne},
|
|
ReturnBool: true,
|
|
}
|
|
}
|
|
;
|
|
|
|
on_or_ignoring : bool_modifier IGNORING grouping_labels
|
|
{
|
|
$$ = $1
|
|
$$.(*BinaryExpr).VectorMatching.MatchingLabels = $3
|
|
}
|
|
| bool_modifier ON grouping_labels
|
|
{
|
|
$$ = $1
|
|
$$.(*BinaryExpr).VectorMatching.MatchingLabels = $3
|
|
$$.(*BinaryExpr).VectorMatching.On = true
|
|
}
|
|
;
|
|
|
|
group_modifiers: bool_modifier /* empty */
|
|
| on_or_ignoring /* empty */
|
|
| on_or_ignoring GROUP_LEFT maybe_grouping_labels
|
|
{
|
|
$$ = $1
|
|
$$.(*BinaryExpr).VectorMatching.Card = CardManyToOne
|
|
$$.(*BinaryExpr).VectorMatching.Include = $3
|
|
}
|
|
| on_or_ignoring GROUP_RIGHT maybe_grouping_labels
|
|
{
|
|
$$ = $1
|
|
$$.(*BinaryExpr).VectorMatching.Card = CardOneToMany
|
|
$$.(*BinaryExpr).VectorMatching.Include = $3
|
|
}
|
|
;
|
|
|
|
|
|
grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
|
|
{ $$ = $2 }
|
|
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
|
|
{ $$ = $2 }
|
|
| LEFT_PAREN RIGHT_PAREN
|
|
{ $$ = []string{} }
|
|
| error
|
|
{ yylex.(*parser).unexpected("grouping opts", "\"(\""); $$ = nil }
|
|
;
|
|
|
|
|
|
grouping_label_list:
|
|
grouping_label_list COMMA grouping_label
|
|
{ $$ = append($1, $3.Val) }
|
|
| grouping_label
|
|
{ $$ = []string{$1.Val} }
|
|
| grouping_label_list error
|
|
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \")\""); $$ = $1 }
|
|
;
|
|
|
|
grouping_label : maybe_label
|
|
{
|
|
if !model.LabelName($1.Val).IsValid() {
|
|
yylex.(*parser).unexpected("grouping opts", "label")
|
|
}
|
|
$$ = $1
|
|
}
|
|
| STRING {
|
|
if !model.LabelName(yylex.(*parser).unquoteString($1.Val)).IsValid() {
|
|
yylex.(*parser).unexpected("grouping opts", "label")
|
|
}
|
|
$$ = $1
|
|
$$.Pos++
|
|
$$.Val = yylex.(*parser).unquoteString($$.Val)
|
|
}
|
|
| error
|
|
{ yylex.(*parser).unexpected("grouping opts", "label"); $$ = Item{} }
|
|
;
|
|
|
|
/*
|
|
* Function calls.
|
|
*/
|
|
|
|
function_call : IDENTIFIER function_call_body
|
|
{
|
|
fn, exist := getFunction($1.Val, yylex.(*parser).functions)
|
|
if !exist{
|
|
yylex.(*parser).addParseErrf($1.PositionRange(),"unknown function with name %q", $1.Val)
|
|
}
|
|
if fn != nil && fn.Experimental && !EnableExperimentalFunctions {
|
|
yylex.(*parser).addParseErrf($1.PositionRange(),"function %q is not enabled", $1.Val)
|
|
}
|
|
$$ = &Call{
|
|
Func: fn,
|
|
Args: $2.(Expressions),
|
|
PosRange: posrange.PositionRange{
|
|
Start: $1.Pos,
|
|
End: yylex.(*parser).lastClosing,
|
|
},
|
|
}
|
|
}
|
|
;
|
|
|
|
function_call_body: LEFT_PAREN function_call_args RIGHT_PAREN
|
|
{ $$ = $2 }
|
|
| LEFT_PAREN RIGHT_PAREN
|
|
{$$ = Expressions{}}
|
|
;
|
|
|
|
function_call_args: function_call_args COMMA expr
|
|
{ $$ = append($1.(Expressions), $3.(Expr)) }
|
|
| expr
|
|
{ $$ = Expressions{$1.(Expr)} }
|
|
| function_call_args COMMA
|
|
{
|
|
yylex.(*parser).addParseErrf($2.PositionRange(), "trailing commas not allowed in function call args")
|
|
$$ = $1
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Expressions inside parentheses.
|
|
*/
|
|
|
|
paren_expr : LEFT_PAREN expr RIGHT_PAREN
|
|
{ $$ = &ParenExpr{Expr: $2.(Expr), PosRange: mergeRanges(&$1, &$3)} }
|
|
;
|
|
|
|
/*
|
|
* Offset modifiers.
|
|
*/
|
|
|
|
offset_expr: expr OFFSET number_duration_literal
|
|
{
|
|
numLit, _ := $3.(*NumberLiteral)
|
|
dur := time.Duration(numLit.Val * 1000) * time.Millisecond
|
|
yylex.(*parser).addOffset($1, dur)
|
|
$$ = $1
|
|
}
|
|
| expr OFFSET SUB number_duration_literal
|
|
{
|
|
numLit, _ := $4.(*NumberLiteral)
|
|
dur := time.Duration(numLit.Val * 1000) * time.Millisecond
|
|
yylex.(*parser).addOffset($1, -dur)
|
|
$$ = $1
|
|
}
|
|
| expr OFFSET error
|
|
{ yylex.(*parser).unexpected("offset", "number or duration"); $$ = $1 }
|
|
;
|
|
/*
|
|
* @ modifiers.
|
|
*/
|
|
|
|
step_invariant_expr: expr AT signed_or_unsigned_number
|
|
{
|
|
yylex.(*parser).setTimestamp($1, $3)
|
|
$$ = $1
|
|
}
|
|
| expr AT at_modifier_preprocessors LEFT_PAREN RIGHT_PAREN
|
|
{
|
|
yylex.(*parser).setAtModifierPreprocessor($1, $3)
|
|
$$ = $1
|
|
}
|
|
| expr AT error
|
|
{ yylex.(*parser).unexpected("@", "timestamp"); $$ = $1 }
|
|
;
|
|
|
|
at_modifier_preprocessors: START | END;
|
|
|
|
/*
|
|
* Subquery and range selectors.
|
|
*/
|
|
|
|
matrix_selector : expr LEFT_BRACKET number_duration_literal RIGHT_BRACKET
|
|
{
|
|
var errMsg string
|
|
vs, ok := $1.(*VectorSelector)
|
|
if !ok{
|
|
errMsg = "ranges only allowed for vector selectors"
|
|
} else if vs.OriginalOffset != 0{
|
|
errMsg = "no offset modifiers allowed before range"
|
|
} else if vs.Timestamp != nil {
|
|
errMsg = "no @ modifiers allowed before range"
|
|
}
|
|
|
|
if errMsg != ""{
|
|
errRange := mergeRanges(&$2, &$4)
|
|
yylex.(*parser).addParseErrf(errRange, errMsg)
|
|
}
|
|
|
|
numLit, _ := $3.(*NumberLiteral)
|
|
$$ = &MatrixSelector{
|
|
VectorSelector: $1.(Expr),
|
|
Range: time.Duration(numLit.Val * 1000) * time.Millisecond,
|
|
EndPos: yylex.(*parser).lastClosing,
|
|
}
|
|
}
|
|
;
|
|
|
|
subquery_expr : expr LEFT_BRACKET number_duration_literal COLON number_duration_literal RIGHT_BRACKET
|
|
{
|
|
numLitRange, _ := $3.(*NumberLiteral)
|
|
numLitStep, _ := $5.(*NumberLiteral)
|
|
$$ = &SubqueryExpr{
|
|
Expr: $1.(Expr),
|
|
Range: time.Duration(numLitRange.Val * 1000) * time.Millisecond,
|
|
Step: time.Duration(numLitStep.Val * 1000) * time.Millisecond,
|
|
EndPos: $6.Pos + 1,
|
|
}
|
|
}
|
|
| expr LEFT_BRACKET number_duration_literal COLON RIGHT_BRACKET
|
|
{
|
|
numLitRange, _ := $3.(*NumberLiteral)
|
|
$$ = &SubqueryExpr{
|
|
Expr: $1.(Expr),
|
|
Range: time.Duration(numLitRange.Val * 1000) * time.Millisecond,
|
|
Step: 0,
|
|
EndPos: $5.Pos + 1,
|
|
}
|
|
}
|
|
| expr LEFT_BRACKET number_duration_literal COLON number_duration_literal error
|
|
{ yylex.(*parser).unexpected("subquery selector", "\"]\""); $$ = $1 }
|
|
| expr LEFT_BRACKET number_duration_literal COLON error
|
|
{ yylex.(*parser).unexpected("subquery selector", "number or duration or \"]\""); $$ = $1 }
|
|
| expr LEFT_BRACKET number_duration_literal error
|
|
{ yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\""); $$ = $1 }
|
|
| expr LEFT_BRACKET error
|
|
{ yylex.(*parser).unexpected("subquery selector", "number or duration"); $$ = $1 }
|
|
;
|
|
|
|
/*
|
|
* Unary expressions.
|
|
*/
|
|
|
|
unary_expr :
|
|
/* Gives the rule the same precedence as MUL. This aligns with mathematical conventions. */
|
|
unary_op expr %prec MUL
|
|
{
|
|
if nl, ok := $2.(*NumberLiteral); ok {
|
|
if $1.Typ == SUB {
|
|
nl.Val *= -1
|
|
}
|
|
nl.PosRange.Start = $1.Pos
|
|
$$ = nl
|
|
} else {
|
|
$$ = &UnaryExpr{Op: $1.Typ, Expr: $2.(Expr), StartPos: $1.Pos}
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Vector selectors.
|
|
*/
|
|
|
|
vector_selector: metric_identifier label_matchers
|
|
{
|
|
vs := $2.(*VectorSelector)
|
|
vs.PosRange = mergeRanges(&$1, vs)
|
|
vs.Name = $1.Val
|
|
yylex.(*parser).assembleVectorSelector(vs)
|
|
$$ = vs
|
|
}
|
|
| metric_identifier
|
|
{
|
|
vs := &VectorSelector{
|
|
Name: $1.Val,
|
|
LabelMatchers: []*labels.Matcher{},
|
|
PosRange: $1.PositionRange(),
|
|
}
|
|
yylex.(*parser).assembleVectorSelector(vs)
|
|
$$ = vs
|
|
}
|
|
| label_matchers
|
|
{
|
|
vs := $1.(*VectorSelector)
|
|
yylex.(*parser).assembleVectorSelector(vs)
|
|
$$ = vs
|
|
}
|
|
;
|
|
|
|
label_matchers : LEFT_BRACE label_match_list RIGHT_BRACE
|
|
{
|
|
$$ = &VectorSelector{
|
|
LabelMatchers: $2,
|
|
PosRange: mergeRanges(&$1, &$3),
|
|
}
|
|
}
|
|
| LEFT_BRACE label_match_list COMMA RIGHT_BRACE
|
|
{
|
|
$$ = &VectorSelector{
|
|
LabelMatchers: $2,
|
|
PosRange: mergeRanges(&$1, &$4),
|
|
}
|
|
}
|
|
| LEFT_BRACE RIGHT_BRACE
|
|
{
|
|
$$ = &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{},
|
|
PosRange: mergeRanges(&$1, &$2),
|
|
}
|
|
}
|
|
;
|
|
|
|
label_match_list: label_match_list COMMA label_matcher
|
|
{
|
|
if $1 != nil{
|
|
$$ = append($1, $3)
|
|
} else {
|
|
$$ = $1
|
|
}
|
|
}
|
|
| label_matcher
|
|
{ $$ = []*labels.Matcher{$1}}
|
|
| label_match_list error
|
|
{ yylex.(*parser).unexpected("label matching", "\",\" or \"}\""); $$ = $1 }
|
|
;
|
|
|
|
label_matcher : IDENTIFIER match_op STRING
|
|
{ $$ = yylex.(*parser).newLabelMatcher($1, $2, $3); }
|
|
| string_identifier match_op STRING
|
|
{ $$ = yylex.(*parser).newLabelMatcher($1, $2, $3); }
|
|
| string_identifier
|
|
{ $$ = yylex.(*parser).newMetricNameMatcher($1); }
|
|
| string_identifier match_op error
|
|
{ yylex.(*parser).unexpected("label matching", "string"); $$ = nil}
|
|
| IDENTIFIER match_op error
|
|
{ yylex.(*parser).unexpected("label matching", "string"); $$ = nil}
|
|
| IDENTIFIER error
|
|
{ yylex.(*parser).unexpected("label matching", "label matching operator"); $$ = nil }
|
|
| error
|
|
{ yylex.(*parser).unexpected("label matching", "identifier or \"}\""); $$ = nil}
|
|
;
|
|
|
|
/*
|
|
* Metric descriptions.
|
|
*/
|
|
|
|
metric : metric_identifier label_set
|
|
{ b := labels.NewBuilder($2); b.Set(labels.MetricName, $1.Val); $$ = b.Labels() }
|
|
| label_set
|
|
{$$ = $1}
|
|
;
|
|
|
|
|
|
metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO;
|
|
|
|
label_set : LEFT_BRACE label_set_list RIGHT_BRACE
|
|
{ $$ = labels.New($2...) }
|
|
| LEFT_BRACE label_set_list COMMA RIGHT_BRACE
|
|
{ $$ = labels.New($2...) }
|
|
| LEFT_BRACE RIGHT_BRACE
|
|
{ $$ = labels.New() }
|
|
| /* empty */
|
|
{ $$ = labels.New() }
|
|
;
|
|
|
|
label_set_list : label_set_list COMMA label_set_item
|
|
{ $$ = append($1, $3) }
|
|
| label_set_item
|
|
{ $$ = []labels.Label{$1} }
|
|
| label_set_list error
|
|
{ yylex.(*parser).unexpected("label set", "\",\" or \"}\"", ); $$ = $1 }
|
|
|
|
;
|
|
|
|
label_set_item : IDENTIFIER EQL STRING
|
|
{ $$ = labels.Label{Name: $1.Val, Value: yylex.(*parser).unquoteString($3.Val) } }
|
|
| string_identifier EQL STRING
|
|
{ $$ = labels.Label{Name: $1.Val, Value: yylex.(*parser).unquoteString($3.Val) } }
|
|
| IDENTIFIER EQL error
|
|
{ yylex.(*parser).unexpected("label set", "string"); $$ = labels.Label{}}
|
|
| string_identifier EQL error
|
|
{ yylex.(*parser).unexpected("label set", "string"); $$ = labels.Label{}}
|
|
| IDENTIFIER error
|
|
{ yylex.(*parser).unexpected("label set", "\"=\""); $$ = labels.Label{}}
|
|
| string_identifier error
|
|
{ yylex.(*parser).unexpected("label set", "\"=\""); $$ = labels.Label{}}
|
|
| error
|
|
{ yylex.(*parser).unexpected("label set", "identifier or \"}\""); $$ = labels.Label{} }
|
|
;
|
|
|
|
/*
|
|
* Series descriptions:
|
|
* A separate language that is used to generate series values promtool.
|
|
* It is included in the promQL parser, because it shares common functionality, such as parsing a metric.
|
|
* The syntax is described in https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/#series
|
|
*/
|
|
|
|
series_description: metric series_values
|
|
{
|
|
yylex.(*parser).generatedParserResult = &seriesDescription{
|
|
labels: $1,
|
|
values: $2,
|
|
}
|
|
}
|
|
;
|
|
|
|
series_values : /*empty*/
|
|
{ $$ = []SequenceValue{} }
|
|
| series_values SPACE series_item
|
|
{ $$ = append($1, $3...) }
|
|
| series_values SPACE
|
|
{ $$ = $1 }
|
|
| error
|
|
{ yylex.(*parser).unexpected("series values", ""); $$ = nil }
|
|
;
|
|
|
|
series_item : BLANK
|
|
{ $$ = []SequenceValue{{Omitted: true}}}
|
|
| BLANK TIMES uint
|
|
{
|
|
$$ = []SequenceValue{}
|
|
for i:=uint64(0); i < $3; i++{
|
|
$$ = append($$, SequenceValue{Omitted: true})
|
|
}
|
|
}
|
|
| series_value
|
|
{ $$ = []SequenceValue{{Value: $1}}}
|
|
| series_value TIMES uint
|
|
{
|
|
$$ = []SequenceValue{}
|
|
// Add an additional value for time 0, which we ignore in tests.
|
|
for i:=uint64(0); i <= $3; i++{
|
|
$$ = append($$, SequenceValue{Value: $1})
|
|
}
|
|
}
|
|
| series_value signed_number TIMES uint
|
|
{
|
|
$$ = []SequenceValue{}
|
|
// Add an additional value for time 0, which we ignore in tests.
|
|
for i:=uint64(0); i <= $4; i++{
|
|
$$ = append($$, SequenceValue{Value: $1})
|
|
$1 += $2
|
|
}
|
|
}
|
|
// Histogram descriptions (part of unit testing).
|
|
| histogram_series_value
|
|
{
|
|
$$ = []SequenceValue{{Histogram:$1}}
|
|
}
|
|
| histogram_series_value TIMES uint
|
|
{
|
|
$$ = []SequenceValue{}
|
|
// Add an additional value for time 0, which we ignore in tests.
|
|
for i:=uint64(0); i <= $3; i++{
|
|
$$ = append($$, SequenceValue{Histogram:$1})
|
|
//$1 += $2
|
|
}
|
|
}
|
|
| histogram_series_value ADD histogram_series_value TIMES uint
|
|
{
|
|
val, err := yylex.(*parser).histogramsIncreaseSeries($1,$3,$5)
|
|
if err != nil {
|
|
yylex.(*parser).addSemanticError(err)
|
|
}
|
|
$$ = val
|
|
}
|
|
| histogram_series_value SUB histogram_series_value TIMES uint
|
|
{
|
|
val, err := yylex.(*parser).histogramsDecreaseSeries($1,$3,$5)
|
|
if err != nil {
|
|
yylex.(*parser).addSemanticError(err)
|
|
}
|
|
$$ = val
|
|
}
|
|
;
|
|
|
|
series_value : IDENTIFIER
|
|
{
|
|
if $1.Val != "stale" {
|
|
yylex.(*parser).unexpected("series values", "number or \"stale\"")
|
|
}
|
|
$$ = math.Float64frombits(value.StaleNaN)
|
|
}
|
|
| number
|
|
| signed_number
|
|
;
|
|
|
|
histogram_series_value
|
|
: OPEN_HIST histogram_desc_map SPACE CLOSE_HIST
|
|
{
|
|
$$ = yylex.(*parser).buildHistogramFromMap(&$2)
|
|
}
|
|
| OPEN_HIST histogram_desc_map CLOSE_HIST
|
|
{
|
|
$$ = yylex.(*parser).buildHistogramFromMap(&$2)
|
|
}
|
|
| OPEN_HIST SPACE CLOSE_HIST
|
|
{
|
|
m := yylex.(*parser).newMap()
|
|
$$ = yylex.(*parser).buildHistogramFromMap(&m)
|
|
}
|
|
| OPEN_HIST CLOSE_HIST
|
|
{
|
|
m := yylex.(*parser).newMap()
|
|
$$ = yylex.(*parser).buildHistogramFromMap(&m)
|
|
}
|
|
;
|
|
|
|
histogram_desc_map
|
|
: histogram_desc_map SPACE histogram_desc_item
|
|
{
|
|
$$ = *(yylex.(*parser).mergeMaps(&$1,&$3))
|
|
}
|
|
| histogram_desc_item
|
|
{
|
|
$$ = $1
|
|
}
|
|
| histogram_desc_map error {
|
|
yylex.(*parser).unexpected("histogram description", "histogram description key, e.g. buckets:[5 10 7]")
|
|
}
|
|
;
|
|
|
|
histogram_desc_item
|
|
: SCHEMA_DESC COLON int
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["schema"] = $3
|
|
}
|
|
| SUM_DESC COLON signed_or_unsigned_number
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["sum"] = $3
|
|
}
|
|
| COUNT_DESC COLON signed_or_unsigned_number
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["count"] = $3
|
|
}
|
|
| ZERO_BUCKET_DESC COLON signed_or_unsigned_number
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["z_bucket"] = $3
|
|
}
|
|
| ZERO_BUCKET_WIDTH_DESC COLON number
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["z_bucket_w"] = $3
|
|
}
|
|
| CUSTOM_VALUES_DESC COLON bucket_set
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["custom_values"] = $3
|
|
}
|
|
| BUCKETS_DESC COLON bucket_set
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["buckets"] = $3
|
|
}
|
|
| OFFSET_DESC COLON int
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["offset"] = $3
|
|
}
|
|
| NEGATIVE_BUCKETS_DESC COLON bucket_set
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["n_buckets"] = $3
|
|
}
|
|
| NEGATIVE_OFFSET_DESC COLON int
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["n_offset"] = $3
|
|
}
|
|
| COUNTER_RESET_HINT_DESC COLON counter_reset_hint
|
|
{
|
|
$$ = yylex.(*parser).newMap()
|
|
$$["counter_reset_hint"] = $3
|
|
}
|
|
;
|
|
|
|
bucket_set : LEFT_BRACKET bucket_set_list SPACE RIGHT_BRACKET
|
|
{
|
|
$$ = $2
|
|
}
|
|
| LEFT_BRACKET bucket_set_list RIGHT_BRACKET
|
|
{
|
|
$$ = $2
|
|
}
|
|
;
|
|
|
|
bucket_set_list : bucket_set_list SPACE signed_or_unsigned_number
|
|
{
|
|
$$ = append($1, $3)
|
|
}
|
|
| signed_or_unsigned_number
|
|
{
|
|
$$ = []float64{$1}
|
|
}
|
|
| bucket_set_list error
|
|
;
|
|
|
|
counter_reset_hint : UNKNOWN_COUNTER_RESET | COUNTER_RESET | NOT_COUNTER_RESET | GAUGE_TYPE;
|
|
|
|
/*
|
|
* Keyword lists.
|
|
*/
|
|
|
|
aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK | LIMITK | LIMIT_RATIO;
|
|
|
|
// Inside of grouping options label names can be recognized as keywords by the lexer. This is a list of keywords that could also be a label name.
|
|
maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO;
|
|
|
|
unary_op : ADD | SUB;
|
|
|
|
match_op : EQL | NEQ | EQL_REGEX | NEQ_REGEX ;
|
|
|
|
/*
|
|
* Literals.
|
|
*/
|
|
|
|
number_duration_literal : NUMBER
|
|
{
|
|
$$ = &NumberLiteral{
|
|
Val: yylex.(*parser).number($1.Val),
|
|
PosRange: $1.PositionRange(),
|
|
}
|
|
}
|
|
| DURATION
|
|
{
|
|
var err error
|
|
var dur time.Duration
|
|
dur, err = parseDuration($1.Val)
|
|
if err != nil {
|
|
yylex.(*parser).addParseErr($1.PositionRange(), err)
|
|
}
|
|
$$ = &NumberLiteral{
|
|
Val: dur.Seconds(),
|
|
PosRange: $1.PositionRange(),
|
|
}
|
|
}
|
|
;
|
|
|
|
number : NUMBER
|
|
{
|
|
$$ = yylex.(*parser).number($1.Val)
|
|
}
|
|
| DURATION
|
|
{
|
|
var err error
|
|
var dur time.Duration
|
|
dur, err = parseDuration($1.Val)
|
|
if err != nil {
|
|
yylex.(*parser).addParseErr($1.PositionRange(), err)
|
|
}
|
|
$$ = dur.Seconds()
|
|
}
|
|
;
|
|
|
|
signed_number : ADD number { $$ = $2 }
|
|
| SUB number { $$ = -$2 }
|
|
;
|
|
|
|
signed_or_unsigned_number: number | signed_number ;
|
|
|
|
uint : NUMBER
|
|
{
|
|
var err error
|
|
$$, err = strconv.ParseUint($1.Val, 10, 64)
|
|
if err != nil {
|
|
yylex.(*parser).addParseErrf($1.PositionRange(), "invalid repetition in series values: %s", err)
|
|
}
|
|
}
|
|
;
|
|
|
|
int : SUB uint { $$ = -int64($2) }
|
|
| uint { $$ = int64($1) }
|
|
;
|
|
|
|
string_literal : STRING
|
|
{
|
|
$$ = &StringLiteral{
|
|
Val: yylex.(*parser).unquoteString($1.Val),
|
|
PosRange: $1.PositionRange(),
|
|
}
|
|
}
|
|
;
|
|
|
|
string_identifier : STRING
|
|
{
|
|
$$ = Item{
|
|
Typ: METRIC_IDENTIFIER,
|
|
Pos: $1.PositionRange().Start,
|
|
Val: yylex.(*parser).unquoteString($1.Val),
|
|
}
|
|
}
|
|
;
|
|
|
|
/*
|
|
* Wrappers for optional arguments.
|
|
*/
|
|
|
|
maybe_grouping_labels: /* empty */ { $$ = nil }
|
|
| grouping_labels
|
|
;
|
|
|
|
%%
|