mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
PromQL: Make parser completely generated (#6548)
Signed-off-by: Tobias Guggenmos <tguggenm@redhat.com>
This commit is contained in:
parent
b7376f3bff
commit
3d6cf1c289
|
@ -18,6 +18,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/prometheus/prometheus/pkg/labels"
|
"github.com/prometheus/prometheus/pkg/labels"
|
||||||
"github.com/prometheus/prometheus/pkg/value"
|
"github.com/prometheus/prometheus/pkg/value"
|
||||||
|
@ -35,202 +36,264 @@ import (
|
||||||
series []sequenceValue
|
series []sequenceValue
|
||||||
uint uint64
|
uint uint64
|
||||||
float float64
|
float float64
|
||||||
|
string string
|
||||||
|
duration time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
%token <item> ERROR
|
%token <item>
|
||||||
%token <item> EOF
|
ASSIGN
|
||||||
%token <item> COMMENT
|
BLANK
|
||||||
%token <item> IDENTIFIER
|
COLON
|
||||||
%token <item> METRIC_IDENTIFIER
|
COMMA
|
||||||
%token <item> LEFT_PAREN
|
COMMENT
|
||||||
%token <item> RIGHT_PAREN
|
DURATION
|
||||||
%token <item> LEFT_BRACE
|
EOF
|
||||||
%token <item> RIGHT_BRACE
|
ERROR
|
||||||
%token <item> LEFT_BRACKET
|
IDENTIFIER
|
||||||
%token <item> RIGHT_BRACKET
|
LEFT_BRACE
|
||||||
%token <item> COMMA
|
LEFT_BRACKET
|
||||||
%token <item> ASSIGN
|
LEFT_PAREN
|
||||||
%token <item> COLON
|
METRIC_IDENTIFIER
|
||||||
%token <item> SEMICOLON
|
NUMBER
|
||||||
%token <item> STRING
|
RIGHT_BRACE
|
||||||
%token <item> NUMBER
|
RIGHT_BRACKET
|
||||||
%token <item> DURATION
|
RIGHT_PAREN
|
||||||
%token <item> BLANK
|
SEMICOLON
|
||||||
%token <item> TIMES
|
SPACE
|
||||||
%token <item> SPACE
|
STRING
|
||||||
|
TIMES
|
||||||
|
|
||||||
%token operatorsStart
|
|
||||||
// Operators.
|
// Operators.
|
||||||
%token <item> SUB
|
%token operatorsStart
|
||||||
%token <item> ADD
|
%token <item>
|
||||||
%token <item> MUL
|
ADD
|
||||||
%token <item> MOD
|
DIV
|
||||||
%token <item> DIV
|
EQL
|
||||||
%token <item> LAND
|
EQL_REGEX
|
||||||
%token <item> LOR
|
GTE
|
||||||
%token <item> LUNLESS
|
GTR
|
||||||
%token <item> EQL
|
LAND
|
||||||
%token <item> NEQ
|
LOR
|
||||||
%token <item> LTE
|
LSS
|
||||||
%token <item> LSS
|
LTE
|
||||||
%token <item> GTE
|
LUNLESS
|
||||||
%token <item> GTR
|
MOD
|
||||||
%token <item> EQL_REGEX
|
MUL
|
||||||
%token <item> NEQ_REGEX
|
NEQ
|
||||||
%token <item> POW
|
NEQ_REGEX
|
||||||
|
POW
|
||||||
|
SUB
|
||||||
%token operatorsEnd
|
%token operatorsEnd
|
||||||
|
|
||||||
%token aggregatorsStart
|
|
||||||
// Aggregators.
|
// Aggregators.
|
||||||
%token <item> AVG
|
%token aggregatorsStart
|
||||||
%token <item> COUNT
|
%token <item>
|
||||||
%token <item> SUM
|
AVG
|
||||||
%token <item> MIN
|
BOTTOMK
|
||||||
%token <item> MAX
|
COUNT
|
||||||
%token <item> STDDEV
|
COUNT_VALUES
|
||||||
%token <item> STDVAR
|
MAX
|
||||||
%token <item> TOPK
|
MIN
|
||||||
%token <item> BOTTOMK
|
QUANTILE
|
||||||
%token <item> COUNT_VALUES
|
STDDEV
|
||||||
%token <item> QUANTILE
|
STDVAR
|
||||||
|
SUM
|
||||||
|
TOPK
|
||||||
%token aggregatorsEnd
|
%token aggregatorsEnd
|
||||||
|
|
||||||
%token keywordsStart
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
%token <item> OFFSET
|
%token keywordsStart
|
||||||
%token <item> BY
|
%token <item>
|
||||||
%token <item> WITHOUT
|
BOOL
|
||||||
%token <item> ON
|
BY
|
||||||
%token <item> IGNORING
|
GROUP_LEFT
|
||||||
%token <item> GROUP_LEFT
|
GROUP_RIGHT
|
||||||
%token <item> GROUP_RIGHT
|
IGNORING
|
||||||
%token <item> BOOL
|
OFFSET
|
||||||
|
ON
|
||||||
|
WITHOUT
|
||||||
%token keywordsEnd
|
%token keywordsEnd
|
||||||
|
|
||||||
|
|
||||||
%token startSymbolsStart
|
|
||||||
// Start symbols for the generated parser.
|
// Start symbols for the generated parser.
|
||||||
%token START_LABELS
|
%token startSymbolsStart
|
||||||
%token START_METRIC
|
%token
|
||||||
%token START_GROUPING_LABELS
|
START_METRIC
|
||||||
%token START_SERIES_DESCRIPTION
|
START_SERIES_DESCRIPTION
|
||||||
|
START_EXPRESSION
|
||||||
|
START_METRIC_SELECTOR
|
||||||
%token startSymbolsEnd
|
%token startSymbolsEnd
|
||||||
|
|
||||||
%type <matchers> label_matchers label_match_list
|
|
||||||
|
// Type definitions for grammar rules.
|
||||||
|
%type <matchers> label_match_list label_matchers
|
||||||
%type <matcher> label_matcher
|
%type <matcher> label_matcher
|
||||||
|
|
||||||
%type <item> match_op metric_identifier grouping_label maybe_label
|
%type <item> aggregate_op grouping_label match_op maybe_label metric_identifier unary_op
|
||||||
|
|
||||||
%type <labels> label_set_list label_set metric
|
%type <labels> label_set label_set_list metric
|
||||||
%type <label> label_set_item
|
%type <label> label_set_item
|
||||||
%type <strings> grouping_labels grouping_label_list
|
%type <strings> grouping_label_list grouping_labels maybe_grouping_labels
|
||||||
%type <series> series_values series_item
|
%type <series> series_item series_values
|
||||||
%type <uint> uint
|
%type <uint> uint
|
||||||
%type <float> series_value signed_number number
|
%type <float> number series_value signed_number
|
||||||
|
%type <node> aggregate_expr aggregate_modifier bin_modifier binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers matrix_selector number_literal offset_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector
|
||||||
|
%type <string> string
|
||||||
|
%type <duration> duration maybe_duration
|
||||||
|
|
||||||
%start start
|
%start start
|
||||||
|
|
||||||
|
// Operators are listed with increasing precedence.
|
||||||
|
%left LOR
|
||||||
|
%left LAND LUNLESS
|
||||||
|
%left EQL GTE GTR LSS LTE NEQ
|
||||||
|
%left ADD SUB
|
||||||
|
%left MUL DIV MOD
|
||||||
|
%right POW
|
||||||
|
|
||||||
|
// Fake token for giving unary expressions maximal precendence
|
||||||
|
%token PREC_MAX
|
||||||
|
%nonassoc PREC_MAX
|
||||||
|
|
||||||
|
// 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_LABELS label_matchers
|
start :
|
||||||
{yylex.(*parser).generatedParserResult.(*VectorSelector).LabelMatchers = $2}
|
START_METRIC metric
|
||||||
| START_METRIC metric
|
|
||||||
{ yylex.(*parser).generatedParserResult = $2 }
|
|
||||||
| START_GROUPING_LABELS grouping_labels
|
|
||||||
{ yylex.(*parser).generatedParserResult = $2 }
|
{ yylex.(*parser).generatedParserResult = $2 }
|
||||||
| START_SERIES_DESCRIPTION series_description
|
| START_SERIES_DESCRIPTION series_description
|
||||||
|
| START_EXPRESSION /* empty */ EOF
|
||||||
|
{ yylex.(*parser).errorf("no expression found in input")}
|
||||||
|
| START_EXPRESSION expr
|
||||||
|
{ yylex.(*parser).generatedParserResult = $2 }
|
||||||
|
| START_METRIC_SELECTOR vector_selector
|
||||||
|
{ yylex.(*parser).generatedParserResult = $2 }
|
||||||
| start EOF
|
| start EOF
|
||||||
| error /* If none of the more detailed error messages are triggered, we fall back to this. */
|
| error /* If none of the more detailed error messages are triggered, we fall back to this. */
|
||||||
{ yylex.(*parser).unexpected("","") }
|
{ yylex.(*parser).unexpected("","") }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
expr :
|
||||||
label_matchers :
|
aggregate_expr
|
||||||
LEFT_BRACE label_match_list RIGHT_BRACE
|
| binary_expr
|
||||||
{ $$ = $2 }
|
| function_call
|
||||||
| LEFT_BRACE label_match_list COMMA RIGHT_BRACE
|
| matrix_selector
|
||||||
{ $$ = $2 }
|
| number_literal
|
||||||
| LEFT_BRACE RIGHT_BRACE
|
| offset_expr
|
||||||
{ $$ = []*labels.Matcher{} }
|
| paren_expr
|
||||||
|
| string_literal
|
||||||
|
| subquery_expr
|
||||||
|
| unary_expr
|
||||||
|
| vector_selector
|
||||||
;
|
;
|
||||||
|
|
||||||
label_match_list:
|
/*
|
||||||
label_match_list COMMA label_matcher
|
* Aggregations.
|
||||||
{ $$ = append($1, $3)}
|
*/
|
||||||
| label_matcher
|
|
||||||
{ $$ = []*labels.Matcher{$1}}
|
aggregate_expr : aggregate_op aggregate_modifier function_call_body
|
||||||
| label_match_list error
|
{ $$ = yylex.(*parser).newAggregateExpr($1, $2, $3) }
|
||||||
{ yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") }
|
| 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","") }
|
||||||
;
|
;
|
||||||
|
|
||||||
label_matcher :
|
aggregate_modifier:
|
||||||
IDENTIFIER match_op STRING
|
BY grouping_labels
|
||||||
{ $$ = yylex.(*parser).newLabelMatcher($1, $2, $3) }
|
{
|
||||||
| IDENTIFIER match_op error
|
$$ = &AggregateExpr{
|
||||||
{ yylex.(*parser).unexpected("label matching", "string")}
|
Grouping: $2,
|
||||||
| IDENTIFIER error
|
}
|
||||||
{ yylex.(*parser).unexpected("label matching", "label matching operator") }
|
}
|
||||||
| error
|
| WITHOUT grouping_labels
|
||||||
{ yylex.(*parser).unexpected("label matching", "identifier or \"}\"")}
|
{
|
||||||
|
$$ = &AggregateExpr{
|
||||||
|
Grouping: $2,
|
||||||
|
Without: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
match_op :
|
/*
|
||||||
EQL {$$ =$1}
|
* Binary expressions.
|
||||||
| NEQ {$$=$1}
|
*/
|
||||||
| EQL_REGEX {$$=$1}
|
|
||||||
| NEQ_REGEX {$$=$1}
|
// 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 DIV bin_modifier expr { $$ = yylex.(*parser).newBinaryExpression($1, $2, $3, $4) }
|
||||||
|
| expr EQL 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
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
metric :
|
grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
|
||||||
metric_identifier label_set
|
|
||||||
{ $$ = append($2, labels.Label{Name: labels.MetricName, Value: $1.Val}); sort.Sort($$) }
|
|
||||||
| label_set
|
|
||||||
{$$ = $1}
|
|
||||||
;
|
|
||||||
|
|
||||||
metric_identifier
|
|
||||||
:
|
|
||||||
METRIC_IDENTIFIER {$$=$1}
|
|
||||||
| IDENTIFIER {$$=$1}
|
|
||||||
|
|
||||||
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 \"}\"", ) }
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
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")}
|
|
||||||
| IDENTIFIER error
|
|
||||||
{ yylex.(*parser).unexpected("label set", "\"=\"")}
|
|
||||||
| error
|
|
||||||
{ yylex.(*parser).unexpected("label set", "identifier or \"}\"") }
|
|
||||||
;
|
|
||||||
|
|
||||||
grouping_labels :
|
|
||||||
LEFT_PAREN grouping_label_list RIGHT_PAREN
|
|
||||||
{ $$ = $2 }
|
{ $$ = $2 }
|
||||||
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
|
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
|
||||||
{ $$ = $2 }
|
{ $$ = $2 }
|
||||||
|
@ -247,11 +310,10 @@ grouping_label_list:
|
||||||
| grouping_label
|
| grouping_label
|
||||||
{ $$ = []string{$1.Val} }
|
{ $$ = []string{$1.Val} }
|
||||||
| grouping_label_list error
|
| grouping_label_list error
|
||||||
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \"}\"") }
|
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \")\"") }
|
||||||
;
|
;
|
||||||
|
|
||||||
grouping_label :
|
grouping_label : maybe_label
|
||||||
maybe_label
|
|
||||||
{
|
{
|
||||||
if !isLabel($1.Val) {
|
if !isLabel($1.Val) {
|
||||||
yylex.(*parser).unexpected("grouping opts", "label")
|
yylex.(*parser).unexpected("grouping opts", "label")
|
||||||
|
@ -262,37 +324,201 @@ grouping_label :
|
||||||
{ yylex.(*parser).unexpected("grouping opts", "label") }
|
{ yylex.(*parser).unexpected("grouping opts", "label") }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function calls.
|
||||||
|
*/
|
||||||
|
|
||||||
/* inside of grouping options label names can be recognized as keywords by the lexer */
|
function_call : IDENTIFIER function_call_body
|
||||||
maybe_label :
|
{
|
||||||
IDENTIFIER
|
fn, exist := getFunction($1.Val)
|
||||||
| METRIC_IDENTIFIER
|
if !exist{
|
||||||
| LAND
|
yylex.(*parser).errorf("unknown function with name %q", $1.Val)
|
||||||
| LOR
|
}
|
||||||
| LUNLESS
|
$$ = &Call{
|
||||||
| AVG
|
Func: fn,
|
||||||
| COUNT
|
Args: $2.(Expressions),
|
||||||
| SUM
|
}
|
||||||
| MIN
|
}
|
||||||
| MAX
|
|
||||||
| STDDEV
|
|
||||||
| STDVAR
|
|
||||||
| TOPK
|
|
||||||
| BOTTOMK
|
|
||||||
| COUNT_VALUES
|
|
||||||
| QUANTILE
|
|
||||||
| OFFSET
|
|
||||||
| BY
|
|
||||||
| ON
|
|
||||||
| IGNORING
|
|
||||||
| GROUP_LEFT
|
|
||||||
| GROUP_RIGHT
|
|
||||||
| BOOL
|
|
||||||
;
|
;
|
||||||
|
|
||||||
// The series description grammar is only used inside unit tests.
|
function_call_body: LEFT_PAREN function_call_args RIGHT_PAREN
|
||||||
series_description:
|
{ $$ = $2 }
|
||||||
metric series_values
|
| LEFT_PAREN RIGHT_PAREN
|
||||||
|
{$$ = Expressions{}}
|
||||||
|
;
|
||||||
|
|
||||||
|
function_call_args: function_call_args COMMA expr
|
||||||
|
{ $$ = append($1.(Expressions), $3.(Expr)) }
|
||||||
|
| expr
|
||||||
|
{ $$ = Expressions{$1.(Expr)} }
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expressions inside parentheses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
paren_expr : LEFT_PAREN expr RIGHT_PAREN
|
||||||
|
{ $$ = &ParenExpr{Expr: $2.(Expr)} }
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Offset modifiers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
offset_expr: expr OFFSET duration
|
||||||
|
{
|
||||||
|
yylex.(*parser).addOffset($1, $3)
|
||||||
|
$$ = $1
|
||||||
|
}
|
||||||
|
| expr OFFSET error
|
||||||
|
{ yylex.(*parser).unexpected("offset", "duration") }
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Subquery and range selectors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
matrix_selector : expr LEFT_BRACKET duration RIGHT_BRACKET
|
||||||
|
{
|
||||||
|
vs, ok := $1.(*VectorSelector)
|
||||||
|
if !ok{
|
||||||
|
yylex.(*parser).errorf("ranges only allowed for vector selectors")
|
||||||
|
}
|
||||||
|
if vs.Offset != 0{
|
||||||
|
yylex.(*parser).errorf("no offset modifiers allowed before range")
|
||||||
|
}
|
||||||
|
$$ = &MatrixSelector{
|
||||||
|
Name: vs.Name,
|
||||||
|
Offset: vs.Offset,
|
||||||
|
LabelMatchers: vs.LabelMatchers,
|
||||||
|
Range: $3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
subquery_expr : expr LEFT_BRACKET duration COLON maybe_duration RIGHT_BRACKET
|
||||||
|
{
|
||||||
|
$$ = &SubqueryExpr{
|
||||||
|
Expr: $1.(Expr),
|
||||||
|
Range: $3,
|
||||||
|
Step: $5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| expr LEFT_BRACKET duration COLON duration error
|
||||||
|
{ yylex.(*parser).unexpected("subquery selector", "\"]\"") }
|
||||||
|
| expr LEFT_BRACKET duration COLON error
|
||||||
|
{ yylex.(*parser).unexpected("subquery selector", "duration or \"]\"") }
|
||||||
|
| expr LEFT_BRACKET duration error
|
||||||
|
{ yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") }
|
||||||
|
| expr LEFT_BRACKET error
|
||||||
|
{ yylex.(*parser).unexpected("subquery selector", "duration") }
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unary expressions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unary_expr :
|
||||||
|
/* gives the rule the same prec as POW. This has the effect that unary opts are always evaluated with highest precedence */
|
||||||
|
unary_op expr %prec PREC_MAX
|
||||||
|
{
|
||||||
|
if nl, ok := $2.(*NumberLiteral); ok {
|
||||||
|
if $1.Typ == SUB {
|
||||||
|
nl.Val *= -1
|
||||||
|
}
|
||||||
|
$$ = nl
|
||||||
|
} else {
|
||||||
|
$$ = &UnaryExpr{Op: $1.Typ, Expr: $2.(Expr)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Vector selectors.
|
||||||
|
*/
|
||||||
|
|
||||||
|
vector_selector: metric_identifier label_matchers
|
||||||
|
{ $$ = yylex.(*parser).newVectorSelector($1.Val, $2) }
|
||||||
|
| metric_identifier
|
||||||
|
{ $$ = yylex.(*parser).newVectorSelector($1.Val, nil) }
|
||||||
|
| label_matchers
|
||||||
|
{ $$ = yylex.(*parser).newVectorSelector("", $1) }
|
||||||
|
;
|
||||||
|
|
||||||
|
label_matchers : LEFT_BRACE label_match_list RIGHT_BRACE
|
||||||
|
{ $$ = $2 }
|
||||||
|
| LEFT_BRACE label_match_list COMMA RIGHT_BRACE
|
||||||
|
{ $$ = $2 }
|
||||||
|
| LEFT_BRACE RIGHT_BRACE
|
||||||
|
{ $$ = []*labels.Matcher{} }
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
label_match_list: label_match_list COMMA label_matcher
|
||||||
|
{ $$ = append($1, $3)}
|
||||||
|
| label_matcher
|
||||||
|
{ $$ = []*labels.Matcher{$1}}
|
||||||
|
| label_match_list error
|
||||||
|
{ yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") }
|
||||||
|
;
|
||||||
|
|
||||||
|
label_matcher : IDENTIFIER match_op STRING
|
||||||
|
{ $$ = yylex.(*parser).newLabelMatcher($1, $2, $3) }
|
||||||
|
| IDENTIFIER match_op error
|
||||||
|
{ yylex.(*parser).unexpected("label matching", "string")}
|
||||||
|
| IDENTIFIER error
|
||||||
|
{ yylex.(*parser).unexpected("label matching", "label matching operator") }
|
||||||
|
| error
|
||||||
|
{ yylex.(*parser).unexpected("label matching", "identifier or \"}\"")}
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Metric descriptions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
metric : metric_identifier label_set
|
||||||
|
{ $$ = append($2, labels.Label{Name: labels.MetricName, Value: $1.Val}); sort.Sort($$) }
|
||||||
|
| label_set
|
||||||
|
{$$ = $1}
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
metric_identifier: METRIC_IDENTIFIER | IDENTIFIER;
|
||||||
|
|
||||||
|
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 \"}\"", ) }
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
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")}
|
||||||
|
| IDENTIFIER error
|
||||||
|
{ yylex.(*parser).unexpected("label set", "\"=\"")}
|
||||||
|
| error
|
||||||
|
{ yylex.(*parser).unexpected("label set", "identifier or \"}\"") }
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Series descriptions (only used by unit tests).
|
||||||
|
*/
|
||||||
|
|
||||||
|
series_description: metric series_values
|
||||||
{
|
{
|
||||||
yylex.(*parser).generatedParserResult = &seriesDescription{
|
yylex.(*parser).generatedParserResult = &seriesDescription{
|
||||||
labels: $1,
|
labels: $1,
|
||||||
|
@ -301,8 +527,7 @@ series_description:
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
series_values :
|
series_values : /*empty*/
|
||||||
/*empty*/
|
|
||||||
{ $$ = []sequenceValue{} }
|
{ $$ = []sequenceValue{} }
|
||||||
| series_values SPACE series_item
|
| series_values SPACE series_item
|
||||||
{ $$ = append($1, $3...) }
|
{ $$ = append($1, $3...) }
|
||||||
|
@ -312,8 +537,7 @@ series_values :
|
||||||
{ yylex.(*parser).unexpected("series values", "") }
|
{ yylex.(*parser).unexpected("series values", "") }
|
||||||
;
|
;
|
||||||
|
|
||||||
series_item :
|
series_item : BLANK
|
||||||
BLANK
|
|
||||||
{ $$ = []sequenceValue{{omitted: true}}}
|
{ $$ = []sequenceValue{{omitted: true}}}
|
||||||
| BLANK TIMES uint
|
| BLANK TIMES uint
|
||||||
{
|
{
|
||||||
|
@ -339,26 +563,9 @@ series_item :
|
||||||
$1 += $2
|
$1 += $2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint :
|
|
||||||
NUMBER
|
|
||||||
{
|
|
||||||
var err error
|
|
||||||
$$, err = strconv.ParseUint($1.Val, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
yylex.(*parser).errorf("invalid repitition in series values: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
;
|
||||||
|
|
||||||
signed_number :
|
series_value : IDENTIFIER
|
||||||
ADD number
|
|
||||||
{ $$ = $2 }
|
|
||||||
| SUB number
|
|
||||||
{ $$ = -$2 }
|
|
||||||
;
|
|
||||||
|
|
||||||
series_value :
|
|
||||||
IDENTIFIER
|
|
||||||
{
|
{
|
||||||
if $1.Val != "stale" {
|
if $1.Val != "stale" {
|
||||||
yylex.(*parser).unexpected("series values", "number or \"stale\"")
|
yylex.(*parser).unexpected("series values", "number or \"stale\"")
|
||||||
|
@ -366,16 +573,73 @@ series_value :
|
||||||
$$ = math.Float64frombits(value.StaleNaN)
|
$$ = math.Float64frombits(value.StaleNaN)
|
||||||
}
|
}
|
||||||
| number
|
| number
|
||||||
{ $$ = $1 }
|
|
||||||
| signed_number
|
| signed_number
|
||||||
{ $$ = $1 }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
number :
|
|
||||||
NUMBER
|
/*
|
||||||
{$$ = yylex.(*parser).number($1.Val) }
|
* Keyword lists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | 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_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK;
|
||||||
|
|
||||||
|
unary_op : ADD | SUB;
|
||||||
|
|
||||||
|
match_op : EQL | NEQ | EQL_REGEX | NEQ_REGEX ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Literals.
|
||||||
|
*/
|
||||||
|
|
||||||
|
number_literal : number { $$ = &NumberLiteral{$1}} ;
|
||||||
|
|
||||||
|
number : NUMBER { $$ = yylex.(*parser).number($1.Val) } ;
|
||||||
|
|
||||||
|
signed_number : ADD number { $$ = $2 }
|
||||||
|
| SUB number { $$ = -$2 }
|
||||||
|
;
|
||||||
|
|
||||||
|
uint : NUMBER
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
$$, err = strconv.ParseUint($1.Val, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
yylex.(*parser).errorf("invalid repetition in series values: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
duration : DURATION
|
||||||
|
{
|
||||||
|
var err error
|
||||||
|
$$, err = parseDuration($1.Val)
|
||||||
|
if err != nil {
|
||||||
|
yylex.(*parser).error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
string_literal : string { $$ = &StringLiteral{$1}} ;
|
||||||
|
|
||||||
|
string : STRING { $$ = yylex.(*parser).unquoteString($1.Val) } ;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wrappers for optional arguments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
maybe_duration : /* empty */
|
||||||
|
{$$ = 0}
|
||||||
|
| duration
|
||||||
|
;
|
||||||
|
|
||||||
|
maybe_grouping_labels: /* empty */ { $$ = nil }
|
||||||
|
| grouping_labels
|
||||||
;
|
;
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -89,38 +89,6 @@ func (i ItemType) isSetOperator() bool {
|
||||||
// LowestPrec is a constant for operator precedence in expressions.
|
// LowestPrec is a constant for operator precedence in expressions.
|
||||||
const LowestPrec = 0 // Non-operators.
|
const LowestPrec = 0 // Non-operators.
|
||||||
|
|
||||||
// Precedence returns the operator precedence of the binary
|
|
||||||
// operator op. If op is not a binary operator, the result
|
|
||||||
// is LowestPrec.
|
|
||||||
func (i ItemType) precedence() int {
|
|
||||||
switch i {
|
|
||||||
case LOR:
|
|
||||||
return 1
|
|
||||||
case LAND, LUNLESS:
|
|
||||||
return 2
|
|
||||||
case EQL, NEQ, LTE, LSS, GTE, GTR:
|
|
||||||
return 3
|
|
||||||
case ADD, SUB:
|
|
||||||
return 4
|
|
||||||
case MUL, DIV, MOD:
|
|
||||||
return 5
|
|
||||||
case POW:
|
|
||||||
return 6
|
|
||||||
default:
|
|
||||||
return LowestPrec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i ItemType) isRightAssociative() bool {
|
|
||||||
switch i {
|
|
||||||
case POW:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
type ItemType int
|
type ItemType int
|
||||||
|
|
||||||
// This is a list of all keywords in PromQL.
|
// This is a list of all keywords in PromQL.
|
||||||
|
|
579
promql/parse.go
579
promql/parse.go
|
@ -54,14 +54,14 @@ func (e *ParseErr) Error() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseExpr returns the expression parsed from the input.
|
// ParseExpr returns the expression parsed from the input.
|
||||||
func ParseExpr(input string) (Expr, error) {
|
func ParseExpr(input string) (expr Expr, err error) {
|
||||||
p := newParser(input)
|
p := newParser(input)
|
||||||
|
|
||||||
expr, err := p.parseExpr()
|
defer p.recover(&err)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
expr = p.parseGenerated(START_EXPRESSION, []ItemType{EOF}).(Expr)
|
||||||
}
|
|
||||||
err = p.typecheck(expr)
|
err = p.typecheck(expr)
|
||||||
|
|
||||||
return expr, err
|
return expr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ func ParseMetric(input string) (m labels.Labels, err error) {
|
||||||
p := newParser(input)
|
p := newParser(input)
|
||||||
defer p.recover(&err)
|
defer p.recover(&err)
|
||||||
|
|
||||||
return p.parseGenerated(START_METRIC, []ItemType{RIGHT_BRACE, EOF}).(labels.Labels), nil
|
return p.parseGenerated(START_METRIC, []ItemType{EOF}).(labels.Labels), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseMetricSelector parses the provided textual metric selector into a list of
|
// ParseMetricSelector parses the provided textual metric selector into a list of
|
||||||
|
@ -79,15 +79,7 @@ func ParseMetricSelector(input string) (m []*labels.Matcher, err error) {
|
||||||
p := newParser(input)
|
p := newParser(input)
|
||||||
defer p.recover(&err)
|
defer p.recover(&err)
|
||||||
|
|
||||||
name := ""
|
return p.parseGenerated(START_METRIC_SELECTOR, []ItemType{EOF}).(*VectorSelector).LabelMatchers, nil
|
||||||
if t := p.peek().Typ; t == METRIC_IDENTIFIER || t == IDENTIFIER {
|
|
||||||
name = p.next().Val
|
|
||||||
}
|
|
||||||
vs := p.VectorSelector(name)
|
|
||||||
if p.peek().Typ != EOF {
|
|
||||||
p.errorf("could not parse remaining input %.15q...", p.lex.input[p.lex.lastPos:])
|
|
||||||
}
|
|
||||||
return vs.LabelMatchers, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// newParser returns a new parser.
|
// newParser returns a new parser.
|
||||||
|
@ -98,23 +90,6 @@ func newParser(input string) *parser {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseExpr parses a single expression from the input.
|
|
||||||
func (p *parser) parseExpr() (expr Expr, err error) {
|
|
||||||
defer p.recover(&err)
|
|
||||||
|
|
||||||
for p.peek().Typ != EOF {
|
|
||||||
if expr != nil {
|
|
||||||
p.errorf("could not parse remaining input %.15q...", p.lex.input[p.lex.lastPos:])
|
|
||||||
}
|
|
||||||
expr = p.expr()
|
|
||||||
}
|
|
||||||
|
|
||||||
if expr == nil {
|
|
||||||
p.errorf("no expression found in input")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// sequenceValue is an omittable value in a sequence of time series values.
|
// sequenceValue is an omittable value in a sequence of time series values.
|
||||||
type sequenceValue struct {
|
type sequenceValue struct {
|
||||||
value float64
|
value float64
|
||||||
|
@ -176,27 +151,6 @@ func (p *parser) next() Item {
|
||||||
return p.token
|
return p.token
|
||||||
}
|
}
|
||||||
|
|
||||||
// peek returns but does not consume the next token.
|
|
||||||
func (p *parser) peek() Item {
|
|
||||||
if p.peeking {
|
|
||||||
return p.token
|
|
||||||
}
|
|
||||||
p.peeking = true
|
|
||||||
|
|
||||||
t := p.lex.NextItem()
|
|
||||||
// Skip comments.
|
|
||||||
for t.Typ == COMMENT {
|
|
||||||
t = p.lex.NextItem()
|
|
||||||
}
|
|
||||||
p.token = t
|
|
||||||
return p.token
|
|
||||||
}
|
|
||||||
|
|
||||||
// backup backs the input stream up one token.
|
|
||||||
func (p *parser) backup() {
|
|
||||||
p.peeking = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// errorf formats the error and terminates processing.
|
// errorf formats the error and terminates processing.
|
||||||
func (p *parser) errorf(format string, args ...interface{}) {
|
func (p *parser) errorf(format string, args ...interface{}) {
|
||||||
p.error(errors.Errorf(format, args...))
|
p.error(errors.Errorf(format, args...))
|
||||||
|
@ -237,25 +191,6 @@ func (p *parser) unexpected(context string, expected string) {
|
||||||
p.error(errors.New(errMsg.String()))
|
p.error(errors.New(errMsg.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// expect consumes the next token and guarantees it has the required type.
|
|
||||||
func (p *parser) expect(exp ItemType, context string) Item {
|
|
||||||
token := p.next()
|
|
||||||
if token.Typ != exp {
|
|
||||||
p.unexpected(context, exp.desc())
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
// expectOneOf consumes the next token and guarantees it has one of the required types.
|
|
||||||
func (p *parser) expectOneOf(exp1, exp2 ItemType, context string) Item {
|
|
||||||
token := p.next()
|
|
||||||
if token.Typ != exp1 && token.Typ != exp2 {
|
|
||||||
expected := exp1.desc() + " or " + exp2.desc()
|
|
||||||
p.unexpected(context, expected)
|
|
||||||
}
|
|
||||||
return token
|
|
||||||
}
|
|
||||||
|
|
||||||
var errUnexpected = errors.New("unexpected error")
|
var errUnexpected = errors.New("unexpected error")
|
||||||
|
|
||||||
// recover is the handler that turns panics into returns from the top level of Parse.
|
// recover is the handler that turns panics into returns from the top level of Parse.
|
||||||
|
@ -329,436 +264,55 @@ func (p *parser) InjectItem(typ ItemType) {
|
||||||
p.inject = Item{Typ: typ}
|
p.inject = Item{Typ: typ}
|
||||||
p.injecting = true
|
p.injecting = true
|
||||||
}
|
}
|
||||||
|
func (p *parser) newBinaryExpression(lhs Node, op Item, modifiers Node, rhs Node) *BinaryExpr {
|
||||||
|
ret := modifiers.(*BinaryExpr)
|
||||||
|
|
||||||
// expr parses any expression.
|
ret.LHS = lhs.(Expr)
|
||||||
func (p *parser) expr() Expr {
|
ret.RHS = rhs.(Expr)
|
||||||
// Parse the starting expression.
|
ret.Op = op.Typ
|
||||||
expr := p.unaryExpr()
|
|
||||||
|
|
||||||
// Loop through the operations and construct a binary operation tree based
|
if ret.ReturnBool && !op.Typ.isComparisonOperator() {
|
||||||
// on the operators' precedence.
|
|
||||||
for {
|
|
||||||
// If the next token is not an operator the expression is done.
|
|
||||||
op := p.peek().Typ
|
|
||||||
if !op.isOperator() {
|
|
||||||
// Check for subquery.
|
|
||||||
if op == LEFT_BRACKET {
|
|
||||||
expr = p.subqueryOrRangeSelector(expr, false)
|
|
||||||
if s, ok := expr.(*SubqueryExpr); ok {
|
|
||||||
// Parse optional offset.
|
|
||||||
if p.peek().Typ == OFFSET {
|
|
||||||
offset := p.offset()
|
|
||||||
s.Offset = offset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
p.next() // Consume operator.
|
|
||||||
|
|
||||||
// Parse optional operator matching options. Its validity
|
|
||||||
// is checked in the type-checking stage.
|
|
||||||
vecMatching := &VectorMatching{
|
|
||||||
Card: CardOneToOne,
|
|
||||||
}
|
|
||||||
if op.isSetOperator() {
|
|
||||||
vecMatching.Card = CardManyToMany
|
|
||||||
}
|
|
||||||
|
|
||||||
returnBool := false
|
|
||||||
// Parse bool modifier.
|
|
||||||
if p.peek().Typ == BOOL {
|
|
||||||
if !op.isComparisonOperator() {
|
|
||||||
p.errorf("bool modifier can only be used on comparison operators")
|
p.errorf("bool modifier can only be used on comparison operators")
|
||||||
}
|
}
|
||||||
p.next()
|
|
||||||
returnBool = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse ON/IGNORING clause.
|
if op.Typ.isComparisonOperator() && !ret.ReturnBool && ret.RHS.Type() == ValueTypeScalar && ret.LHS.Type() == ValueTypeScalar {
|
||||||
if p.peek().Typ == ON || p.peek().Typ == IGNORING {
|
|
||||||
if p.peek().Typ == ON {
|
|
||||||
vecMatching.On = true
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
vecMatching.MatchingLabels = p.labels()
|
|
||||||
|
|
||||||
// Parse grouping.
|
|
||||||
if t := p.peek().Typ; t == GROUP_LEFT || t == GROUP_RIGHT {
|
|
||||||
p.next()
|
|
||||||
if t == GROUP_LEFT {
|
|
||||||
vecMatching.Card = CardManyToOne
|
|
||||||
} else {
|
|
||||||
vecMatching.Card = CardOneToMany
|
|
||||||
}
|
|
||||||
if p.peek().Typ == LEFT_PAREN {
|
|
||||||
vecMatching.Include = p.labels()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ln := range vecMatching.MatchingLabels {
|
|
||||||
for _, ln2 := range vecMatching.Include {
|
|
||||||
if ln == ln2 && vecMatching.On {
|
|
||||||
p.errorf("label %q must not occur in ON and GROUP clause at once", ln)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the next operand.
|
|
||||||
rhs := p.unaryExpr()
|
|
||||||
|
|
||||||
// Assign the new root based on the precedence of the LHS and RHS operators.
|
|
||||||
expr = p.balance(expr, op, rhs, vecMatching, returnBool)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *parser) balance(lhs Expr, op ItemType, rhs Expr, vecMatching *VectorMatching, returnBool bool) *BinaryExpr {
|
|
||||||
if lhsBE, ok := lhs.(*BinaryExpr); ok {
|
|
||||||
precd := lhsBE.Op.precedence() - op.precedence()
|
|
||||||
if (precd < 0) || (precd == 0 && op.isRightAssociative()) {
|
|
||||||
balanced := p.balance(lhsBE.RHS, op, rhs, vecMatching, returnBool)
|
|
||||||
if lhsBE.Op.isComparisonOperator() && !lhsBE.ReturnBool && balanced.Type() == ValueTypeScalar && lhsBE.LHS.Type() == ValueTypeScalar {
|
|
||||||
p.errorf("comparisons between scalars must use BOOL modifier")
|
p.errorf("comparisons between scalars must use BOOL modifier")
|
||||||
}
|
}
|
||||||
return &BinaryExpr{
|
|
||||||
Op: lhsBE.Op,
|
if op.Typ.isSetOperator() && ret.VectorMatching.Card == CardOneToOne {
|
||||||
LHS: lhsBE.LHS,
|
ret.VectorMatching.Card = CardManyToMany
|
||||||
RHS: balanced,
|
|
||||||
VectorMatching: lhsBE.VectorMatching,
|
|
||||||
ReturnBool: lhsBE.ReturnBool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, l1 := range ret.VectorMatching.MatchingLabels {
|
||||||
|
for _, l2 := range ret.VectorMatching.Include {
|
||||||
|
if l1 == l2 && ret.VectorMatching.On {
|
||||||
|
p.errorf("label %q must not occur in ON and GROUP clause at once", l1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if op.isComparisonOperator() && !returnBool && rhs.Type() == ValueTypeScalar && lhs.Type() == ValueTypeScalar {
|
|
||||||
p.errorf("comparisons between scalars must use BOOL modifier")
|
|
||||||
}
|
|
||||||
return &BinaryExpr{
|
|
||||||
Op: op,
|
|
||||||
LHS: lhs,
|
|
||||||
RHS: rhs,
|
|
||||||
VectorMatching: vecMatching,
|
|
||||||
ReturnBool: returnBool,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unaryExpr parses a unary expression.
|
return ret
|
||||||
//
|
|
||||||
// <Vector_selector> | <Matrix_selector> | (+|-) <number_literal> | '(' <expr> ')'
|
|
||||||
//
|
|
||||||
func (p *parser) unaryExpr() Expr {
|
|
||||||
switch t := p.peek(); t.Typ {
|
|
||||||
case ADD, SUB:
|
|
||||||
p.next()
|
|
||||||
e := p.unaryExpr()
|
|
||||||
|
|
||||||
// Simplify unary expressions for number literals.
|
|
||||||
if nl, ok := e.(*NumberLiteral); ok {
|
|
||||||
if t.Typ == SUB {
|
|
||||||
nl.Val *= -1
|
|
||||||
}
|
|
||||||
return nl
|
|
||||||
}
|
|
||||||
return &UnaryExpr{Op: t.Typ, Expr: e}
|
|
||||||
|
|
||||||
case LEFT_PAREN:
|
|
||||||
p.next()
|
|
||||||
e := p.expr()
|
|
||||||
p.expect(RIGHT_PAREN, "paren expression")
|
|
||||||
|
|
||||||
return &ParenExpr{Expr: e}
|
|
||||||
}
|
|
||||||
e := p.primaryExpr()
|
|
||||||
|
|
||||||
// Expression might be followed by a range selector.
|
|
||||||
if p.peek().Typ == LEFT_BRACKET {
|
|
||||||
e = p.subqueryOrRangeSelector(e, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse optional offset.
|
func (p *parser) newVectorSelector(name string, labelMatchers []*labels.Matcher) *VectorSelector {
|
||||||
if p.peek().Typ == OFFSET {
|
ret := &VectorSelector{LabelMatchers: labelMatchers}
|
||||||
offset := p.offset()
|
|
||||||
|
|
||||||
switch s := e.(type) {
|
|
||||||
case *VectorSelector:
|
|
||||||
s.Offset = offset
|
|
||||||
case *MatrixSelector:
|
|
||||||
s.Offset = offset
|
|
||||||
case *SubqueryExpr:
|
|
||||||
s.Offset = offset
|
|
||||||
default:
|
|
||||||
p.errorf("offset modifier must be preceded by an instant or range selector, but follows a %T instead", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
// subqueryOrRangeSelector parses a Subquery based on given Expr (or)
|
|
||||||
// a Matrix (a.k.a. range) selector based on a given Vector selector.
|
|
||||||
//
|
|
||||||
// <Vector_selector> '[' <duration> ']' | <Vector_selector> '[' <duration> ':' [<duration>] ']'
|
|
||||||
//
|
|
||||||
func (p *parser) subqueryOrRangeSelector(expr Expr, checkRange bool) Expr {
|
|
||||||
ctx := "subquery selector"
|
|
||||||
if checkRange {
|
|
||||||
ctx = "range/subquery selector"
|
|
||||||
}
|
|
||||||
|
|
||||||
p.next()
|
|
||||||
|
|
||||||
var erange time.Duration
|
|
||||||
var err error
|
|
||||||
|
|
||||||
erangeStr := p.expect(DURATION, ctx).Val
|
|
||||||
erange, err = parseDuration(erangeStr)
|
|
||||||
if err != nil {
|
|
||||||
p.error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var itm Item
|
|
||||||
if checkRange {
|
|
||||||
itm = p.expectOneOf(RIGHT_BRACKET, COLON, ctx)
|
|
||||||
if itm.Typ == RIGHT_BRACKET {
|
|
||||||
// Range selector.
|
|
||||||
vs, ok := expr.(*VectorSelector)
|
|
||||||
if !ok {
|
|
||||||
p.errorf("range specification must be preceded by a metric selector, but follows a %T instead", expr)
|
|
||||||
}
|
|
||||||
return &MatrixSelector{
|
|
||||||
Name: vs.Name,
|
|
||||||
LabelMatchers: vs.LabelMatchers,
|
|
||||||
Range: erange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
itm = p.expect(COLON, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subquery.
|
|
||||||
var estep time.Duration
|
|
||||||
|
|
||||||
itm = p.expectOneOf(RIGHT_BRACKET, DURATION, ctx)
|
|
||||||
if itm.Typ == DURATION {
|
|
||||||
estepStr := itm.Val
|
|
||||||
estep, err = parseDuration(estepStr)
|
|
||||||
if err != nil {
|
|
||||||
p.error(err)
|
|
||||||
}
|
|
||||||
p.expect(RIGHT_BRACKET, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &SubqueryExpr{
|
|
||||||
Expr: expr,
|
|
||||||
Range: erange,
|
|
||||||
Step: estep,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// number parses a number.
|
|
||||||
func (p *parser) number(val string) float64 {
|
|
||||||
n, err := strconv.ParseInt(val, 0, 64)
|
|
||||||
f := float64(n)
|
|
||||||
if err != nil {
|
|
||||||
f, err = strconv.ParseFloat(val, 64)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
p.errorf("error parsing number: %s", err)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// primaryExpr parses a primary expression.
|
|
||||||
//
|
|
||||||
// <metric_name> | <function_call> | <Vector_aggregation> | <literal>
|
|
||||||
//
|
|
||||||
func (p *parser) primaryExpr() Expr {
|
|
||||||
switch t := p.next(); {
|
|
||||||
case t.Typ == NUMBER:
|
|
||||||
f := p.number(t.Val)
|
|
||||||
return &NumberLiteral{f}
|
|
||||||
|
|
||||||
case t.Typ == STRING:
|
|
||||||
return &StringLiteral{p.unquoteString(t.Val)}
|
|
||||||
|
|
||||||
case t.Typ == LEFT_BRACE:
|
|
||||||
// Metric selector without metric name.
|
|
||||||
p.backup()
|
|
||||||
return p.VectorSelector("")
|
|
||||||
|
|
||||||
case t.Typ == IDENTIFIER:
|
|
||||||
// Check for function call.
|
|
||||||
if p.peek().Typ == LEFT_PAREN {
|
|
||||||
return p.call(t.Val)
|
|
||||||
}
|
|
||||||
fallthrough // Else metric selector.
|
|
||||||
|
|
||||||
case t.Typ == METRIC_IDENTIFIER:
|
|
||||||
return p.VectorSelector(t.Val)
|
|
||||||
|
|
||||||
case t.Typ.isAggregator():
|
|
||||||
p.backup()
|
|
||||||
return p.aggrExpr()
|
|
||||||
|
|
||||||
default:
|
|
||||||
p.errorf("no valid expression found")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// labels parses a list of labelnames.
|
|
||||||
//
|
|
||||||
// '(' <label_name>, ... ')'
|
|
||||||
//
|
|
||||||
func (p *parser) labels() []string {
|
|
||||||
return p.parseGenerated(START_GROUPING_LABELS, []ItemType{RIGHT_PAREN, EOF}).([]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
// aggrExpr parses an aggregation expression.
|
|
||||||
//
|
|
||||||
// <aggr_op> (<Vector_expr>) [by|without <labels>]
|
|
||||||
// <aggr_op> [by|without <labels>] (<Vector_expr>)
|
|
||||||
//
|
|
||||||
func (p *parser) aggrExpr() *AggregateExpr {
|
|
||||||
const ctx = "aggregation"
|
|
||||||
|
|
||||||
agop := p.next()
|
|
||||||
if !agop.Typ.isAggregator() {
|
|
||||||
p.errorf("expected aggregation operator but got %s", agop)
|
|
||||||
}
|
|
||||||
var grouping []string
|
|
||||||
var without bool
|
|
||||||
|
|
||||||
modifiersFirst := false
|
|
||||||
|
|
||||||
if t := p.peek().Typ; t == BY || t == WITHOUT {
|
|
||||||
if t == WITHOUT {
|
|
||||||
without = true
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
grouping = p.labels()
|
|
||||||
modifiersFirst = true
|
|
||||||
}
|
|
||||||
|
|
||||||
p.expect(LEFT_PAREN, ctx)
|
|
||||||
var param Expr
|
|
||||||
if agop.Typ.isAggregatorWithParam() {
|
|
||||||
param = p.expr()
|
|
||||||
p.expect(COMMA, ctx)
|
|
||||||
}
|
|
||||||
e := p.expr()
|
|
||||||
p.expect(RIGHT_PAREN, ctx)
|
|
||||||
|
|
||||||
if !modifiersFirst {
|
|
||||||
if t := p.peek().Typ; t == BY || t == WITHOUT {
|
|
||||||
if len(grouping) > 0 {
|
|
||||||
p.errorf("aggregation must only contain one grouping clause")
|
|
||||||
}
|
|
||||||
if t == WITHOUT {
|
|
||||||
without = true
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
grouping = p.labels()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &AggregateExpr{
|
|
||||||
Op: agop.Typ,
|
|
||||||
Expr: e,
|
|
||||||
Param: param,
|
|
||||||
Grouping: grouping,
|
|
||||||
Without: without,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// call parses a function call.
|
|
||||||
//
|
|
||||||
// <func_name> '(' [ <arg_expr>, ...] ')'
|
|
||||||
//
|
|
||||||
func (p *parser) call(name string) *Call {
|
|
||||||
const ctx = "function call"
|
|
||||||
|
|
||||||
fn, exist := getFunction(name)
|
|
||||||
if !exist {
|
|
||||||
p.errorf("unknown function with name %q", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.expect(LEFT_PAREN, ctx)
|
|
||||||
// Might be call without args.
|
|
||||||
if p.peek().Typ == RIGHT_PAREN {
|
|
||||||
p.next() // Consume.
|
|
||||||
return &Call{fn, nil}
|
|
||||||
}
|
|
||||||
|
|
||||||
var args []Expr
|
|
||||||
for {
|
|
||||||
e := p.expr()
|
|
||||||
args = append(args, e)
|
|
||||||
|
|
||||||
// Terminate if no more arguments.
|
|
||||||
if p.peek().Typ != COMMA {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call must be closed.
|
|
||||||
p.expect(RIGHT_PAREN, ctx)
|
|
||||||
|
|
||||||
return &Call{Func: fn, Args: args}
|
|
||||||
}
|
|
||||||
|
|
||||||
// offset parses an offset modifier.
|
|
||||||
//
|
|
||||||
// offset <duration>
|
|
||||||
//
|
|
||||||
func (p *parser) offset() time.Duration {
|
|
||||||
const ctx = "offset"
|
|
||||||
|
|
||||||
p.next()
|
|
||||||
offi := p.expect(DURATION, ctx)
|
|
||||||
|
|
||||||
offset, err := parseDuration(offi.Val)
|
|
||||||
if err != nil {
|
|
||||||
p.error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// VectorSelector parses a new (instant) vector selector.
|
|
||||||
//
|
|
||||||
// <metric_identifier> [<label_matchers>]
|
|
||||||
// [<metric_identifier>] <label_matchers>
|
|
||||||
//
|
|
||||||
func (p *parser) VectorSelector(name string) *VectorSelector {
|
|
||||||
ret := &VectorSelector{
|
|
||||||
Name: name,
|
|
||||||
}
|
|
||||||
// Parse label matching if any.
|
|
||||||
if t := p.peek(); t.Typ == LEFT_BRACE {
|
|
||||||
p.generatedParserResult = ret
|
|
||||||
|
|
||||||
p.parseGenerated(START_LABELS, []ItemType{RIGHT_BRACE, EOF})
|
|
||||||
}
|
|
||||||
// Metric name must not be set in the label matchers and before at the same time.
|
|
||||||
if name != "" {
|
if name != "" {
|
||||||
|
ret.Name = name
|
||||||
|
|
||||||
for _, m := range ret.LabelMatchers {
|
for _, m := range ret.LabelMatchers {
|
||||||
if m.Name == labels.MetricName {
|
if m.Name == labels.MetricName {
|
||||||
p.errorf("metric name must not be set twice: %q or %q", name, m.Value)
|
p.errorf("metric name must not be set twice: %q or %q", name, m.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set name label matching.
|
|
||||||
m, err := labels.NewMatcher(labels.MatchEqual, labels.MetricName, name)
|
nameMatcher, err := labels.NewMatcher(labels.MatchEqual, labels.MetricName, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // Must not happen with metric.Equal.
|
panic(err) // Must not happen with labels.MatchEqual
|
||||||
}
|
}
|
||||||
ret.LabelMatchers = append(ret.LabelMatchers, m)
|
ret.LabelMatchers = append(ret.LabelMatchers, nameMatcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ret.LabelMatchers) == 0 {
|
|
||||||
p.errorf("vector selector must contain label matchers or metric name")
|
|
||||||
}
|
|
||||||
// A Vector selector must contain at least one non-empty matcher to prevent
|
// A Vector selector must contain at least one non-empty matcher to prevent
|
||||||
// implicit selection of all metrics (e.g. by a typo).
|
// implicit selection of all metrics (e.g. by a typo).
|
||||||
notEmpty := false
|
notEmpty := false
|
||||||
|
@ -775,6 +329,53 @@ func (p *parser) VectorSelector(name string) *VectorSelector {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parser) newAggregateExpr(op Item, modifier Node, args Node) (ret *AggregateExpr) {
|
||||||
|
ret = modifier.(*AggregateExpr)
|
||||||
|
arguments := args.(Expressions)
|
||||||
|
|
||||||
|
ret.Op = op.Typ
|
||||||
|
|
||||||
|
if len(arguments) == 0 {
|
||||||
|
p.errorf("no arguments for aggregate expression provided")
|
||||||
|
|
||||||
|
// Currently p.errorf() panics, so this return is not needed
|
||||||
|
// at the moment.
|
||||||
|
// However, this behaviour is likely to be changed in the
|
||||||
|
// future. In case of having non-panicking errors this
|
||||||
|
// return prevents invalid array accesses
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
desiredArgs := 1
|
||||||
|
if ret.Op.isAggregatorWithParam() {
|
||||||
|
desiredArgs = 2
|
||||||
|
|
||||||
|
ret.Param = arguments[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(arguments) != desiredArgs {
|
||||||
|
p.errorf("wrong number of arguments for aggregate expression provided, expected %d, got %d", desiredArgs, len(arguments))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Expr = arguments[desiredArgs-1]
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// number parses a number.
|
||||||
|
func (p *parser) number(val string) float64 {
|
||||||
|
n, err := strconv.ParseInt(val, 0, 64)
|
||||||
|
f := float64(n)
|
||||||
|
if err != nil {
|
||||||
|
f, err = strconv.ParseFloat(val, 64)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
p.errorf("error parsing number: %s", err)
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
// expectType checks the type of the node and raises an error if it
|
// expectType checks the type of the node and raises an error if it
|
||||||
// is not of the expected type.
|
// is not of the expected type.
|
||||||
func (p *parser) expectType(node Node, want ValueType, context string) {
|
func (p *parser) expectType(node Node, want ValueType, context string) {
|
||||||
|
@ -974,3 +575,27 @@ func (p *parser) newLabelMatcher(label Item, operator Item, value Item) *labels.
|
||||||
|
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *parser) addOffset(e Node, offset time.Duration) {
|
||||||
|
var offsetp *time.Duration
|
||||||
|
|
||||||
|
switch s := e.(type) {
|
||||||
|
case *VectorSelector:
|
||||||
|
offsetp = &s.Offset
|
||||||
|
case *MatrixSelector:
|
||||||
|
offsetp = &s.Offset
|
||||||
|
case *SubqueryExpr:
|
||||||
|
offsetp = &s.Offset
|
||||||
|
default:
|
||||||
|
p.errorf("offset modifier must be preceded by an instant or range selector, but follows a %T instead", e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is already ensured by parseDuration func that there never will be a zero offset modifier
|
||||||
|
if *offsetp != 0 {
|
||||||
|
p.errorf("offset may not be set multiple times")
|
||||||
|
} else {
|
||||||
|
*offsetp = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -102,6 +102,9 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "1 <= bool 1",
|
input: "1 <= bool 1",
|
||||||
expected: &BinaryExpr{LTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
|
expected: &BinaryExpr{LTE, &NumberLiteral{1}, &NumberLiteral{1}, nil, true},
|
||||||
|
}, {
|
||||||
|
input: "-1^2",
|
||||||
|
expected: &BinaryExpr{POW, &NumberLiteral{-1}, &NumberLiteral{2}, nil, false},
|
||||||
}, {
|
}, {
|
||||||
input: "+1 + -2 * 1",
|
input: "+1 + -2 * 1",
|
||||||
expected: &BinaryExpr{
|
expected: &BinaryExpr{
|
||||||
|
@ -171,7 +174,7 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "1+",
|
input: "1+",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "no valid expression found",
|
errMsg: "unexpected end of input",
|
||||||
}, {
|
}, {
|
||||||
input: ".",
|
input: ".",
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -179,11 +182,11 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "2.5.",
|
input: "2.5.",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \".\"...",
|
errMsg: "unexpected character: '.'",
|
||||||
}, {
|
}, {
|
||||||
input: "100..4",
|
input: "100..4",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \".4\"...",
|
errMsg: `unexpected number ".4"`,
|
||||||
}, {
|
}, {
|
||||||
input: "0deadbeef",
|
input: "0deadbeef",
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -191,15 +194,15 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "1 /",
|
input: "1 /",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "no valid expression found",
|
errMsg: "unexpected end of input",
|
||||||
}, {
|
}, {
|
||||||
input: "*1",
|
input: "*1",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "no valid expression found",
|
errMsg: "unexpected <op:*>",
|
||||||
}, {
|
}, {
|
||||||
input: "(1))",
|
input: "(1))",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \")\"...",
|
errMsg: "unexpected \")\"",
|
||||||
}, {
|
}, {
|
||||||
input: "((1)",
|
input: "((1)",
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -231,11 +234,11 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "1 !~ 1",
|
input: "1 !~ 1",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"!~ 1\"...",
|
errMsg: `unexpected character after '!': '~'`,
|
||||||
}, {
|
}, {
|
||||||
input: "1 =~ 1",
|
input: "1 =~ 1",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"=~ 1\"...",
|
errMsg: `unexpected character after '=': '~'`,
|
||||||
}, {
|
}, {
|
||||||
input: `-"string"`,
|
input: `-"string"`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -247,15 +250,19 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `*test`,
|
input: `*test`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "no valid expression found",
|
errMsg: "unexpected <op:*>",
|
||||||
}, {
|
}, {
|
||||||
input: "1 offset 1d",
|
input: "1 offset 1d",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "offset modifier must be preceded by an instant or range selector",
|
errMsg: "offset modifier must be preceded by an instant or range selector",
|
||||||
|
}, {
|
||||||
|
input: "foo offset 1s offset 2s",
|
||||||
|
fail: true,
|
||||||
|
errMsg: "offset may not be set multiple times",
|
||||||
}, {
|
}, {
|
||||||
input: "a - on(b) ignoring(c) d",
|
input: "a - on(b) ignoring(c) d",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:11: parse error: no valid expression found",
|
errMsg: "1:11: parse error: unexpected <ignoring>",
|
||||||
},
|
},
|
||||||
// Vector binary operations.
|
// Vector binary operations.
|
||||||
{
|
{
|
||||||
|
@ -779,6 +786,10 @@ var testExpr = []struct {
|
||||||
input: "foo == on(bar) 10",
|
input: "foo == on(bar) 10",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "vector matching only allowed between instant vectors",
|
errMsg: "vector matching only allowed between instant vectors",
|
||||||
|
}, {
|
||||||
|
input: "foo + group_left(baz) bar",
|
||||||
|
fail: true,
|
||||||
|
errMsg: "unexpected <group_left>",
|
||||||
}, {
|
}, {
|
||||||
input: "foo and on(bar) group_left(baz) bar",
|
input: "foo and on(bar) group_left(baz) bar",
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -910,7 +921,7 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `some}`,
|
input: `some}`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"}\"...",
|
errMsg: "unexpected character: '}'",
|
||||||
}, {
|
}, {
|
||||||
input: `some_metric{a=b}`,
|
input: `some_metric{a=b}`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -944,7 +955,7 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `{}`,
|
input: `{}`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "vector selector must contain label matchers or metric name",
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
||||||
}, {
|
}, {
|
||||||
input: `{x=""}`,
|
input: `{x=""}`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -1086,11 +1097,11 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `some_metric OFFSET 1m[5m]`,
|
input: `some_metric OFFSET 1m[5m]`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:25: parse error: unexpected \"]\" in subquery selector, expected \":\"",
|
errMsg: "1:25: parse error: no offset modifiers allowed before range",
|
||||||
}, {
|
}, {
|
||||||
input: `(foo + bar)[5m]`,
|
input: `(foo + bar)[5m]`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:15: parse error: unexpected \"]\" in subquery selector, expected \":\"",
|
errMsg: "1:15: parse error: ranges only allowed for vector selectors",
|
||||||
},
|
},
|
||||||
// Test aggregation.
|
// Test aggregation.
|
||||||
{
|
{
|
||||||
|
@ -1267,39 +1278,39 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `sum some_metric by (test)`,
|
input: `sum some_metric by (test)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "unexpected identifier \"some_metric\" in aggregation, expected \"(\"",
|
errMsg: "unexpected identifier \"some_metric\" in aggregation",
|
||||||
}, {
|
}, {
|
||||||
input: `sum (some_metric) by test`,
|
input: `sum (some_metric) by test`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
|
errMsg: "unexpected identifier \"test\" in grouping opts",
|
||||||
}, {
|
}, {
|
||||||
input: `sum (some_metric) by test`,
|
input: `sum (some_metric) by test`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
|
errMsg: "unexpected identifier \"test\" in grouping opts",
|
||||||
}, {
|
}, {
|
||||||
input: `sum () by (test)`,
|
input: `sum () by (test)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "no valid expression found",
|
errMsg: "no arguments for aggregate expression provided",
|
||||||
}, {
|
}, {
|
||||||
input: "MIN keep_common (some_metric)",
|
input: "MIN keep_common (some_metric)",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:5: parse error: unexpected identifier \"keep_common\" in aggregation, expected \"(\"",
|
errMsg: "1:5: parse error: unexpected identifier \"keep_common\" in aggregation",
|
||||||
}, {
|
}, {
|
||||||
input: "MIN (some_metric) keep_common",
|
input: "MIN (some_metric) keep_common",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"keep_common\"...",
|
errMsg: `unexpected identifier "keep_common"`,
|
||||||
}, {
|
}, {
|
||||||
input: `sum (some_metric) without (test) by (test)`,
|
input: `sum (some_metric) without (test) by (test)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"by (test)\"...",
|
errMsg: "unexpected <by>",
|
||||||
}, {
|
}, {
|
||||||
input: `sum without (test) (some_metric) by (test)`,
|
input: `sum without (test) (some_metric) by (test)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input \"by (test)\"...",
|
errMsg: "unexpected <by>",
|
||||||
}, {
|
}, {
|
||||||
input: `topk(some_metric)`,
|
input: `topk(some_metric)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:17: parse error: unexpected \")\" in aggregation, expected \",\"",
|
errMsg: "wrong number of arguments for aggregate expression provided, expected 2, got 1",
|
||||||
}, {
|
}, {
|
||||||
input: `topk(some_metric, other_metric)`,
|
input: `topk(some_metric, other_metric)`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -1314,6 +1325,7 @@ var testExpr = []struct {
|
||||||
input: "time()",
|
input: "time()",
|
||||||
expected: &Call{
|
expected: &Call{
|
||||||
Func: mustGetFunction("time"),
|
Func: mustGetFunction("time"),
|
||||||
|
Args: Expressions{},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
input: `floor(some_metric{foo!="bar"})`,
|
input: `floor(some_metric{foo!="bar"})`,
|
||||||
|
@ -1399,15 +1411,15 @@ var testExpr = []struct {
|
||||||
{
|
{
|
||||||
input: "-=",
|
input: "-=",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: `no valid expression found`,
|
errMsg: `unexpected "="`,
|
||||||
}, {
|
}, {
|
||||||
input: "++-++-+-+-<",
|
input: "++-++-+-+-<",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: `no valid expression found`,
|
errMsg: `unexpected <op:<>`,
|
||||||
}, {
|
}, {
|
||||||
input: "e-+=/(0)",
|
input: "e-+=/(0)",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: `no valid expression found`,
|
errMsg: `unexpected "="`,
|
||||||
},
|
},
|
||||||
// String quoting and escape sequence interpretation tests.
|
// String quoting and escape sequence interpretation tests.
|
||||||
{
|
{
|
||||||
|
@ -1443,7 +1455,7 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: "`\\``",
|
input: "`\\``",
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "could not parse remaining input",
|
errMsg: "unterminated raw string",
|
||||||
}, {
|
}, {
|
||||||
input: `"\`,
|
input: `"\`,
|
||||||
fail: true,
|
fail: true,
|
||||||
|
@ -1651,7 +1663,7 @@ var testExpr = []struct {
|
||||||
}, {
|
}, {
|
||||||
input: `(foo + bar{nm="val"})[5m:][10m:5s]`,
|
input: `(foo + bar{nm="val"})[5m:][10m:5s]`,
|
||||||
fail: true,
|
fail: true,
|
||||||
errMsg: "1:27: parse error: could not parse remaining input \"[10m:5s]\"...",
|
errMsg: `1:35: parse error: subquery is only allowed on instant vector, got matrix in "(foo + bar{nm=\"val\"})[5m:][10m:5s]" instead`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,13 +45,13 @@ var scenarios = map[string]struct {
|
||||||
"invalid params from the beginning": {
|
"invalid params from the beginning": {
|
||||||
params: "match[]=-not-a-valid-metric-name",
|
params: "match[]=-not-a-valid-metric-name",
|
||||||
code: 400,
|
code: 400,
|
||||||
body: `1:1: parse error: vector selector must contain label matchers or metric name
|
body: `1:1: parse error: unexpected <op:->
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
"invalid params somewhere in the middle": {
|
"invalid params somewhere in the middle": {
|
||||||
params: "match[]=not-a-valid-metric-name",
|
params: "match[]=not-a-valid-metric-name",
|
||||||
code: 400,
|
code: 400,
|
||||||
body: `1:4: parse error: could not parse remaining input "-a-valid-metric"...
|
body: `1:4: parse error: unexpected <op:->
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
"test_metric1": {
|
"test_metric1": {
|
||||||
|
|
Loading…
Reference in a new issue