prometheus/promql/parser/generated_parser.y
Gregor Zeitlinger f01718262a
Unit tests for native histograms (#12668)
promql: Extend testing framework to support native histograms

This includes both the internal testing framework as well as the rules unit test feature of promtool.

This also adds a bunch of basic tests. Many of the code level tests can now be converted to tests within the framework, and more tests can be added easily.

---------

Signed-off-by: Harold Dost <h.dost@criteo.com>
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
Signed-off-by: Stephen Lang <stephen.lang@grafana.com>
Co-authored-by: Harold Dost <h.dost@criteo.com>
Co-authored-by: Stephen Lang <stephen.lang@grafana.com>
Co-authored-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
2023-08-25 23:35:42 +02:00

916 lines
31 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"
)
%}
%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
duration time.Duration
}
%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
%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
%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
// 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
%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_literal offset_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector
%type <duration> duration maybe_duration
%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(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_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 !isLabel($1.Val) {
yylex.(*parser).unexpected("grouping opts", "label")
}
$$ = $1
}
| 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)
}
$$ = &Call{
Func: fn,
Args: $2.(Expressions),
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 duration
{
yylex.(*parser).addOffset($1, $3)
$$ = $1
}
| expr OFFSET SUB duration
{
yylex.(*parser).addOffset($1, -$4)
$$ = $1
}
| expr OFFSET error
{ yylex.(*parser).unexpected("offset", "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 duration 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)
}
$$ = &MatrixSelector{
VectorSelector: $1.(Expr),
Range: $3,
EndPos: yylex.(*parser).lastClosing,
}
}
;
subquery_expr : expr LEFT_BRACKET duration COLON maybe_duration RIGHT_BRACKET
{
$$ = &SubqueryExpr{
Expr: $1.(Expr),
Range: $3,
Step: $5,
EndPos: $6.Pos + 1,
}
}
| expr LEFT_BRACKET duration COLON duration error
{ yylex.(*parser).unexpected("subquery selector", "\"]\""); $$ = $1 }
| expr LEFT_BRACKET duration COLON error
{ yylex.(*parser).unexpected("subquery selector", "duration or \"]\""); $$ = $1 }
| expr LEFT_BRACKET duration error
{ yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\""); $$ = $1 }
| expr LEFT_BRACKET error
{ yylex.(*parser).unexpected("subquery selector", "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); }
| 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;
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) } }
| IDENTIFIER EQL error
{ yylex.(*parser).unexpected("label set", "string"); $$ = labels.Label{}}
| 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 number
{
$$ = yylex.(*parser).newMap()
$$["count"] = $3
}
| ZERO_BUCKET_DESC COLON number
{
$$ = yylex.(*parser).newMap()
$$["z_bucket"] = $3
}
| ZERO_BUCKET_WIDTH_DESC COLON number
{
$$ = yylex.(*parser).newMap()
$$["z_bucket_w"] = $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
}
;
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 number
{
$$ = append($1, $3)
}
| number
{
$$ = []float64{$1}
}
| bucket_set_list error
;
/*
* Keyword lists.
*/
aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK ;
// 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;
unary_op : ADD | SUB;
match_op : EQL | NEQ | EQL_REGEX | NEQ_REGEX ;
/*
* Literals.
*/
number_literal : NUMBER
{
$$ = &NumberLiteral{
Val: yylex.(*parser).number($1.Val),
PosRange: $1.PositionRange(),
}
}
;
number : NUMBER { $$ = yylex.(*parser).number($1.Val) } ;
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) }
;
duration : DURATION
{
var err error
$$, err = parseDuration($1.Val)
if err != nil {
yylex.(*parser).addParseErr($1.PositionRange(), err)
}
}
;
string_literal : STRING
{
$$ = &StringLiteral{
Val: yylex.(*parser).unquoteString($1.Val),
PosRange: $1.PositionRange(),
}
}
;
/*
* Wrappers for optional arguments.
*/
maybe_duration : /* empty */
{$$ = 0}
| duration
;
maybe_grouping_labels: /* empty */ { $$ = nil }
| grouping_labels
;
%%