mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -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"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/pkg/value"
|
||||
|
@ -35,202 +36,264 @@ import (
|
|||
series []sequenceValue
|
||||
uint uint64
|
||||
float float64
|
||||
string string
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
|
||||
%token <item> ERROR
|
||||
%token <item> EOF
|
||||
%token <item> COMMENT
|
||||
%token <item> IDENTIFIER
|
||||
%token <item> METRIC_IDENTIFIER
|
||||
%token <item> LEFT_PAREN
|
||||
%token <item> RIGHT_PAREN
|
||||
%token <item> LEFT_BRACE
|
||||
%token <item> RIGHT_BRACE
|
||||
%token <item> LEFT_BRACKET
|
||||
%token <item> RIGHT_BRACKET
|
||||
%token <item> COMMA
|
||||
%token <item> ASSIGN
|
||||
%token <item> COLON
|
||||
%token <item> SEMICOLON
|
||||
%token <item> STRING
|
||||
%token <item> NUMBER
|
||||
%token <item> DURATION
|
||||
%token <item> BLANK
|
||||
%token <item> TIMES
|
||||
%token <item> SPACE
|
||||
%token <item>
|
||||
ASSIGN
|
||||
BLANK
|
||||
COLON
|
||||
COMMA
|
||||
COMMENT
|
||||
DURATION
|
||||
EOF
|
||||
ERROR
|
||||
IDENTIFIER
|
||||
LEFT_BRACE
|
||||
LEFT_BRACKET
|
||||
LEFT_PAREN
|
||||
METRIC_IDENTIFIER
|
||||
NUMBER
|
||||
RIGHT_BRACE
|
||||
RIGHT_BRACKET
|
||||
RIGHT_PAREN
|
||||
SEMICOLON
|
||||
SPACE
|
||||
STRING
|
||||
TIMES
|
||||
|
||||
%token operatorsStart
|
||||
// Operators.
|
||||
%token <item> SUB
|
||||
%token <item> ADD
|
||||
%token <item> MUL
|
||||
%token <item> MOD
|
||||
%token <item> DIV
|
||||
%token <item> LAND
|
||||
%token <item> LOR
|
||||
%token <item> LUNLESS
|
||||
%token <item> EQL
|
||||
%token <item> NEQ
|
||||
%token <item> LTE
|
||||
%token <item> LSS
|
||||
%token <item> GTE
|
||||
%token <item> GTR
|
||||
%token <item> EQL_REGEX
|
||||
%token <item> NEQ_REGEX
|
||||
%token <item> POW
|
||||
%token operatorsStart
|
||||
%token <item>
|
||||
ADD
|
||||
DIV
|
||||
EQL
|
||||
EQL_REGEX
|
||||
GTE
|
||||
GTR
|
||||
LAND
|
||||
LOR
|
||||
LSS
|
||||
LTE
|
||||
LUNLESS
|
||||
MOD
|
||||
MUL
|
||||
NEQ
|
||||
NEQ_REGEX
|
||||
POW
|
||||
SUB
|
||||
%token operatorsEnd
|
||||
|
||||
%token aggregatorsStart
|
||||
// Aggregators.
|
||||
%token <item> AVG
|
||||
%token <item> COUNT
|
||||
%token <item> SUM
|
||||
%token <item> MIN
|
||||
%token <item> MAX
|
||||
%token <item> STDDEV
|
||||
%token <item> STDVAR
|
||||
%token <item> TOPK
|
||||
%token <item> BOTTOMK
|
||||
%token <item> COUNT_VALUES
|
||||
%token <item> QUANTILE
|
||||
%token aggregatorsStart
|
||||
%token <item>
|
||||
AVG
|
||||
BOTTOMK
|
||||
COUNT
|
||||
COUNT_VALUES
|
||||
MAX
|
||||
MIN
|
||||
QUANTILE
|
||||
STDDEV
|
||||
STDVAR
|
||||
SUM
|
||||
TOPK
|
||||
%token aggregatorsEnd
|
||||
|
||||
%token keywordsStart
|
||||
// Keywords.
|
||||
%token <item> OFFSET
|
||||
%token <item> BY
|
||||
%token <item> WITHOUT
|
||||
%token <item> ON
|
||||
%token <item> IGNORING
|
||||
%token <item> GROUP_LEFT
|
||||
%token <item> GROUP_RIGHT
|
||||
%token <item> BOOL
|
||||
|
||||
%token keywordsStart
|
||||
%token <item>
|
||||
BOOL
|
||||
BY
|
||||
GROUP_LEFT
|
||||
GROUP_RIGHT
|
||||
IGNORING
|
||||
OFFSET
|
||||
ON
|
||||
WITHOUT
|
||||
%token keywordsEnd
|
||||
|
||||
|
||||
%token startSymbolsStart
|
||||
// Start symbols for the generated parser.
|
||||
%token START_LABELS
|
||||
%token START_METRIC
|
||||
%token START_GROUPING_LABELS
|
||||
%token START_SERIES_DESCRIPTION
|
||||
%token startSymbolsStart
|
||||
%token
|
||||
START_METRIC
|
||||
START_SERIES_DESCRIPTION
|
||||
START_EXPRESSION
|
||||
START_METRIC_SELECTOR
|
||||
%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 <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 <label> label_set_item
|
||||
%type <strings> grouping_labels grouping_label_list
|
||||
%type <series> series_values series_item
|
||||
%type <labels> label_set label_set_list metric
|
||||
%type <label> label_set_item
|
||||
%type <strings> grouping_label_list grouping_labels maybe_grouping_labels
|
||||
%type <series> series_item series_values
|
||||
%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
|
||||
|
||||
// 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
|
||||
{yylex.(*parser).generatedParserResult.(*VectorSelector).LabelMatchers = $2}
|
||||
| START_METRIC metric
|
||||
{ yylex.(*parser).generatedParserResult = $2 }
|
||||
| START_GROUPING_LABELS grouping_labels
|
||||
{ yylex.(*parser).generatedParserResult = $2 }
|
||||
start :
|
||||
START_METRIC metric
|
||||
{ yylex.(*parser).generatedParserResult = $2 }
|
||||
| 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
|
||||
| error /* If none of the more detailed error messages are triggered, we fall back to this. */
|
||||
{ yylex.(*parser).unexpected("","") }
|
||||
;
|
||||
|
||||
|
||||
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{} }
|
||||
|
||||
expr :
|
||||
aggregate_expr
|
||||
| binary_expr
|
||||
| function_call
|
||||
| matrix_selector
|
||||
| number_literal
|
||||
| offset_expr
|
||||
| paren_expr
|
||||
| string_literal
|
||||
| subquery_expr
|
||||
| unary_expr
|
||||
| vector_selector
|
||||
;
|
||||
|
||||
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 \"}\"") }
|
||||
/*
|
||||
* 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","") }
|
||||
;
|
||||
|
||||
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 \"}\"")}
|
||||
aggregate_modifier:
|
||||
BY grouping_labels
|
||||
{
|
||||
$$ = &AggregateExpr{
|
||||
Grouping: $2,
|
||||
}
|
||||
}
|
||||
| WITHOUT grouping_labels
|
||||
{
|
||||
$$ = &AggregateExpr{
|
||||
Grouping: $2,
|
||||
Without: true,
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
match_op :
|
||||
EQL {$$ =$1}
|
||||
| NEQ {$$=$1}
|
||||
| EQL_REGEX {$$=$1}
|
||||
| NEQ_REGEX {$$=$1}
|
||||
/*
|
||||
* 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 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 :
|
||||
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
|
||||
grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN
|
||||
{ $$ = $2 }
|
||||
| LEFT_PAREN grouping_label_list COMMA RIGHT_PAREN
|
||||
{ $$ = $2 }
|
||||
|
@ -247,11 +310,10 @@ grouping_label_list:
|
|||
| grouping_label
|
||||
{ $$ = []string{$1.Val} }
|
||||
| grouping_label_list error
|
||||
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \"}\"") }
|
||||
{ yylex.(*parser).unexpected("grouping opts", "\",\" or \")\"") }
|
||||
;
|
||||
|
||||
grouping_label :
|
||||
maybe_label
|
||||
grouping_label : maybe_label
|
||||
{
|
||||
if !isLabel($1.Val) {
|
||||
yylex.(*parser).unexpected("grouping opts", "label")
|
||||
|
@ -262,37 +324,201 @@ grouping_label :
|
|||
{ yylex.(*parser).unexpected("grouping opts", "label") }
|
||||
;
|
||||
|
||||
|
||||
/* inside of grouping options label names can be recognized as keywords by the lexer */
|
||||
maybe_label :
|
||||
IDENTIFIER
|
||||
| METRIC_IDENTIFIER
|
||||
| LAND
|
||||
| LOR
|
||||
| LUNLESS
|
||||
| AVG
|
||||
| COUNT
|
||||
| SUM
|
||||
| MIN
|
||||
| MAX
|
||||
| STDDEV
|
||||
| STDVAR
|
||||
| TOPK
|
||||
| BOTTOMK
|
||||
| COUNT_VALUES
|
||||
| QUANTILE
|
||||
| OFFSET
|
||||
| BY
|
||||
| ON
|
||||
| IGNORING
|
||||
| GROUP_LEFT
|
||||
| GROUP_RIGHT
|
||||
| BOOL
|
||||
/*
|
||||
* Function calls.
|
||||
*/
|
||||
|
||||
function_call : IDENTIFIER function_call_body
|
||||
{
|
||||
fn, exist := getFunction($1.Val)
|
||||
if !exist{
|
||||
yylex.(*parser).errorf("unknown function with name %q", $1.Val)
|
||||
}
|
||||
$$ = &Call{
|
||||
Func: fn,
|
||||
Args: $2.(Expressions),
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
// The series description grammar is only used inside unit tests.
|
||||
series_description:
|
||||
metric series_values
|
||||
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)} }
|
||||
;
|
||||
|
||||
/*
|
||||
* 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{
|
||||
labels: $1,
|
||||
|
@ -301,8 +527,7 @@ series_description:
|
|||
}
|
||||
;
|
||||
|
||||
series_values :
|
||||
/*empty*/
|
||||
series_values : /*empty*/
|
||||
{ $$ = []sequenceValue{} }
|
||||
| series_values SPACE series_item
|
||||
{ $$ = append($1, $3...) }
|
||||
|
@ -312,8 +537,7 @@ series_values :
|
|||
{ yylex.(*parser).unexpected("series values", "") }
|
||||
;
|
||||
|
||||
series_item :
|
||||
BLANK
|
||||
series_item : BLANK
|
||||
{ $$ = []sequenceValue{{omitted: true}}}
|
||||
| BLANK TIMES uint
|
||||
{
|
||||
|
@ -339,26 +563,9 @@ series_item :
|
|||
$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 :
|
||||
ADD number
|
||||
{ $$ = $2 }
|
||||
| SUB number
|
||||
{ $$ = -$2 }
|
||||
;
|
||||
|
||||
series_value :
|
||||
IDENTIFIER
|
||||
series_value : IDENTIFIER
|
||||
{
|
||||
if $1.Val != "stale" {
|
||||
yylex.(*parser).unexpected("series values", "number or \"stale\"")
|
||||
|
@ -366,16 +573,73 @@ series_value :
|
|||
$$ = math.Float64frombits(value.StaleNaN)
|
||||
}
|
||||
| number
|
||||
{ $$ = $1 }
|
||||
| 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.
|
||||
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
|
||||
|
||||
// This is a list of all keywords in PromQL.
|
||||
|
|
577
promql/parse.go
577
promql/parse.go
|
@ -54,14 +54,14 @@ func (e *ParseErr) Error() string {
|
|||
}
|
||||
|
||||
// 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)
|
||||
|
||||
expr, err := p.parseExpr()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer p.recover(&err)
|
||||
|
||||
expr = p.parseGenerated(START_EXPRESSION, []ItemType{EOF}).(Expr)
|
||||
err = p.typecheck(expr)
|
||||
|
||||
return expr, err
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ func ParseMetric(input string) (m labels.Labels, err error) {
|
|||
p := newParser(input)
|
||||
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
|
||||
|
@ -79,15 +79,7 @@ func ParseMetricSelector(input string) (m []*labels.Matcher, err error) {
|
|||
p := newParser(input)
|
||||
defer p.recover(&err)
|
||||
|
||||
name := ""
|
||||
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
|
||||
return p.parseGenerated(START_METRIC_SELECTOR, []ItemType{EOF}).(*VectorSelector).LabelMatchers, nil
|
||||
}
|
||||
|
||||
// newParser returns a new parser.
|
||||
|
@ -98,23 +90,6 @@ func newParser(input string) *parser {
|
|||
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.
|
||||
type sequenceValue struct {
|
||||
value float64
|
||||
|
@ -176,27 +151,6 @@ func (p *parser) next() Item {
|
|||
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.
|
||||
func (p *parser) errorf(format string, args ...interface{}) {
|
||||
p.error(errors.Errorf(format, args...))
|
||||
|
@ -237,25 +191,6 @@ func (p *parser) unexpected(context string, expected 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")
|
||||
|
||||
// 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.injecting = true
|
||||
}
|
||||
func (p *parser) newBinaryExpression(lhs Node, op Item, modifiers Node, rhs Node) *BinaryExpr {
|
||||
ret := modifiers.(*BinaryExpr)
|
||||
|
||||
// expr parses any expression.
|
||||
func (p *parser) expr() Expr {
|
||||
// Parse the starting expression.
|
||||
expr := p.unaryExpr()
|
||||
ret.LHS = lhs.(Expr)
|
||||
ret.RHS = rhs.(Expr)
|
||||
ret.Op = op.Typ
|
||||
|
||||
// Loop through the operations and construct a binary operation tree based
|
||||
// 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.next()
|
||||
returnBool = true
|
||||
}
|
||||
|
||||
// Parse ON/IGNORING clause.
|
||||
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)
|
||||
if ret.ReturnBool && !op.Typ.isComparisonOperator() {
|
||||
p.errorf("bool modifier can only be used on comparison operators")
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
return &BinaryExpr{
|
||||
Op: lhsBE.Op,
|
||||
LHS: lhsBE.LHS,
|
||||
RHS: balanced,
|
||||
VectorMatching: lhsBE.VectorMatching,
|
||||
ReturnBool: lhsBE.ReturnBool,
|
||||
}
|
||||
}
|
||||
}
|
||||
if op.isComparisonOperator() && !returnBool && rhs.Type() == ValueTypeScalar && lhs.Type() == ValueTypeScalar {
|
||||
if op.Typ.isComparisonOperator() && !ret.ReturnBool && ret.RHS.Type() == ValueTypeScalar && ret.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.
|
||||
//
|
||||
// <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)
|
||||
if op.Typ.isSetOperator() && ret.VectorMatching.Card == CardOneToOne {
|
||||
ret.VectorMatching.Card = CardManyToMany
|
||||
}
|
||||
|
||||
// Parse optional offset.
|
||||
if p.peek().Typ == OFFSET {
|
||||
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,
|
||||
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)
|
||||
}
|
||||
}
|
||||
} 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,
|
||||
}
|
||||
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
|
||||
}
|
||||
func (p *parser) newVectorSelector(name string, labelMatchers []*labels.Matcher) *VectorSelector {
|
||||
ret := &VectorSelector{LabelMatchers: labelMatchers}
|
||||
|
||||
// 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 != "" {
|
||||
ret.Name = name
|
||||
|
||||
for _, m := range ret.LabelMatchers {
|
||||
if m.Name == labels.MetricName {
|
||||
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 {
|
||||
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
|
||||
// implicit selection of all metrics (e.g. by a typo).
|
||||
notEmpty := false
|
||||
|
@ -775,6 +329,53 @@ func (p *parser) VectorSelector(name string) *VectorSelector {
|
|||
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
|
||||
// is not of the expected type.
|
||||
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
|
||||
}
|
||||
|
||||
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",
|
||||
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",
|
||||
expected: &BinaryExpr{
|
||||
|
@ -171,7 +174,7 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: "1+",
|
||||
fail: true,
|
||||
errMsg: "no valid expression found",
|
||||
errMsg: "unexpected end of input",
|
||||
}, {
|
||||
input: ".",
|
||||
fail: true,
|
||||
|
@ -179,11 +182,11 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: "2.5.",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \".\"...",
|
||||
errMsg: "unexpected character: '.'",
|
||||
}, {
|
||||
input: "100..4",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \".4\"...",
|
||||
errMsg: `unexpected number ".4"`,
|
||||
}, {
|
||||
input: "0deadbeef",
|
||||
fail: true,
|
||||
|
@ -191,15 +194,15 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: "1 /",
|
||||
fail: true,
|
||||
errMsg: "no valid expression found",
|
||||
errMsg: "unexpected end of input",
|
||||
}, {
|
||||
input: "*1",
|
||||
fail: true,
|
||||
errMsg: "no valid expression found",
|
||||
errMsg: "unexpected <op:*>",
|
||||
}, {
|
||||
input: "(1))",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \")\"...",
|
||||
errMsg: "unexpected \")\"",
|
||||
}, {
|
||||
input: "((1)",
|
||||
fail: true,
|
||||
|
@ -231,11 +234,11 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: "1 !~ 1",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"!~ 1\"...",
|
||||
errMsg: `unexpected character after '!': '~'`,
|
||||
}, {
|
||||
input: "1 =~ 1",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"=~ 1\"...",
|
||||
errMsg: `unexpected character after '=': '~'`,
|
||||
}, {
|
||||
input: `-"string"`,
|
||||
fail: true,
|
||||
|
@ -247,15 +250,19 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `*test`,
|
||||
fail: true,
|
||||
errMsg: "no valid expression found",
|
||||
errMsg: "unexpected <op:*>",
|
||||
}, {
|
||||
input: "1 offset 1d",
|
||||
fail: true,
|
||||
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",
|
||||
fail: true,
|
||||
errMsg: "1:11: parse error: no valid expression found",
|
||||
errMsg: "1:11: parse error: unexpected <ignoring>",
|
||||
},
|
||||
// Vector binary operations.
|
||||
{
|
||||
|
@ -779,6 +786,10 @@ var testExpr = []struct {
|
|||
input: "foo == on(bar) 10",
|
||||
fail: true,
|
||||
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",
|
||||
fail: true,
|
||||
|
@ -910,7 +921,7 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `some}`,
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"}\"...",
|
||||
errMsg: "unexpected character: '}'",
|
||||
}, {
|
||||
input: `some_metric{a=b}`,
|
||||
fail: true,
|
||||
|
@ -944,7 +955,7 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `{}`,
|
||||
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=""}`,
|
||||
fail: true,
|
||||
|
@ -1086,11 +1097,11 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `some_metric OFFSET 1m[5m]`,
|
||||
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]`,
|
||||
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.
|
||||
{
|
||||
|
@ -1267,39 +1278,39 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `sum some_metric by (test)`,
|
||||
fail: true,
|
||||
errMsg: "unexpected identifier \"some_metric\" in aggregation, expected \"(\"",
|
||||
errMsg: "unexpected identifier \"some_metric\" in aggregation",
|
||||
}, {
|
||||
input: `sum (some_metric) by test`,
|
||||
fail: true,
|
||||
errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
|
||||
errMsg: "unexpected identifier \"test\" in grouping opts",
|
||||
}, {
|
||||
input: `sum (some_metric) by test`,
|
||||
fail: true,
|
||||
errMsg: "unexpected identifier \"test\" in grouping opts, expected \"(\"",
|
||||
errMsg: "unexpected identifier \"test\" in grouping opts",
|
||||
}, {
|
||||
input: `sum () by (test)`,
|
||||
fail: true,
|
||||
errMsg: "no valid expression found",
|
||||
errMsg: "no arguments for aggregate expression provided",
|
||||
}, {
|
||||
input: "MIN keep_common (some_metric)",
|
||||
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",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"keep_common\"...",
|
||||
errMsg: `unexpected identifier "keep_common"`,
|
||||
}, {
|
||||
input: `sum (some_metric) without (test) by (test)`,
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"by (test)\"...",
|
||||
errMsg: "unexpected <by>",
|
||||
}, {
|
||||
input: `sum without (test) (some_metric) by (test)`,
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input \"by (test)\"...",
|
||||
errMsg: "unexpected <by>",
|
||||
}, {
|
||||
input: `topk(some_metric)`,
|
||||
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)`,
|
||||
fail: true,
|
||||
|
@ -1314,6 +1325,7 @@ var testExpr = []struct {
|
|||
input: "time()",
|
||||
expected: &Call{
|
||||
Func: mustGetFunction("time"),
|
||||
Args: Expressions{},
|
||||
},
|
||||
}, {
|
||||
input: `floor(some_metric{foo!="bar"})`,
|
||||
|
@ -1399,15 +1411,15 @@ var testExpr = []struct {
|
|||
{
|
||||
input: "-=",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
errMsg: `unexpected "="`,
|
||||
}, {
|
||||
input: "++-++-+-+-<",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
errMsg: `unexpected <op:<>`,
|
||||
}, {
|
||||
input: "e-+=/(0)",
|
||||
fail: true,
|
||||
errMsg: `no valid expression found`,
|
||||
errMsg: `unexpected "="`,
|
||||
},
|
||||
// String quoting and escape sequence interpretation tests.
|
||||
{
|
||||
|
@ -1443,7 +1455,7 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: "`\\``",
|
||||
fail: true,
|
||||
errMsg: "could not parse remaining input",
|
||||
errMsg: "unterminated raw string",
|
||||
}, {
|
||||
input: `"\`,
|
||||
fail: true,
|
||||
|
@ -1651,7 +1663,7 @@ var testExpr = []struct {
|
|||
}, {
|
||||
input: `(foo + bar{nm="val"})[5m:][10m:5s]`,
|
||||
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": {
|
||||
params: "match[]=-not-a-valid-metric-name",
|
||||
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": {
|
||||
params: "match[]=not-a-valid-metric-name",
|
||||
code: 400,
|
||||
body: `1:4: parse error: could not parse remaining input "-a-valid-metric"...
|
||||
body: `1:4: parse error: unexpected <op:->
|
||||
`,
|
||||
},
|
||||
"test_metric1": {
|
||||
|
|
Loading…
Reference in a new issue