|
|
|
|
@ -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,450 +264,116 @@ 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() |
|
|
|
|
|
|
|
|
|
// 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() |
|
|
|
|
ret.LHS = lhs.(Expr) |
|
|
|
|
ret.RHS = rhs.(Expr) |
|
|
|
|
ret.Op = op.Typ |
|
|
|
|
|
|
|
|
|
// 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} |
|
|
|
|
if op.Typ.isSetOperator() && ret.VectorMatching.Card == CardOneToOne { |
|
|
|
|
ret.VectorMatching.Card = CardManyToMany |
|
|
|
|
} |
|
|
|
|
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.
|
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return e |
|
|
|
|
return ret |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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() |
|
|
|
|
func (p *parser) newVectorSelector(name string, labelMatchers []*labels.Matcher) *VectorSelector { |
|
|
|
|
ret := &VectorSelector{LabelMatchers: labelMatchers} |
|
|
|
|
|
|
|
|
|
var erange time.Duration |
|
|
|
|
var err error |
|
|
|
|
|
|
|
|
|
erangeStr := p.expect(DURATION, ctx).Val |
|
|
|
|
erange, err = parseDuration(erangeStr) |
|
|
|
|
if err != nil { |
|
|
|
|
p.error(err) |
|
|
|
|
} |
|
|
|
|
if name != "" { |
|
|
|
|
ret.Name = name |
|
|
|
|
|
|
|
|
|
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 _, m := range ret.LabelMatchers { |
|
|
|
|
if m.Name == labels.MetricName { |
|
|
|
|
p.errorf("metric name must not be set twice: %q or %q", name, m.Value) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} 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) |
|
|
|
|
nameMatcher, err := labels.NewMatcher(labels.MatchEqual, labels.MetricName, name) |
|
|
|
|
if err != nil { |
|
|
|
|
p.error(err) |
|
|
|
|
panic(err) // Must not happen with labels.MatchEqual
|
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|
ret.LabelMatchers = append(ret.LabelMatchers, nameMatcher) |
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|
// 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 |
|
|
|
|
for _, lm := range ret.LabelMatchers { |
|
|
|
|
if !lm.Matches("") { |
|
|
|
|
notEmpty = true |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
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) |
|
|
|
|
if !notEmpty { |
|
|
|
|
p.errorf("vector selector must contain at least one non-empty matcher") |
|
|
|
|
} |
|
|
|
|
var grouping []string |
|
|
|
|
var without bool |
|
|
|
|
|
|
|
|
|
modifiersFirst := false |
|
|
|
|
return ret |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if t := p.peek().Typ; t == BY || t == WITHOUT { |
|
|
|
|
if t == WITHOUT { |
|
|
|
|
without = true |
|
|
|
|
} |
|
|
|
|
p.next() |
|
|
|
|
grouping = p.labels() |
|
|
|
|
modifiersFirst = true |
|
|
|
|
} |
|
|
|
|
func (p *parser) newAggregateExpr(op Item, modifier Node, args Node) (ret *AggregateExpr) { |
|
|
|
|
ret = modifier.(*AggregateExpr) |
|
|
|
|
arguments := args.(Expressions) |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
ret.Op = op.Typ |
|
|
|
|
|
|
|
|
|
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() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if len(arguments) == 0 { |
|
|
|
|
p.errorf("no arguments for aggregate expression provided") |
|
|
|
|
|
|
|
|
|
return &AggregateExpr{ |
|
|
|
|
Op: agop.Typ, |
|
|
|
|
Expr: e, |
|
|
|
|
Param: param, |
|
|
|
|
Grouping: grouping, |
|
|
|
|
Without: without, |
|
|
|
|
// 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 |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// call parses a function call.
|
|
|
|
|
//
|
|
|
|
|
// <func_name> '(' [ <arg_expr>, ...] ')'
|
|
|
|
|
//
|
|
|
|
|
func (p *parser) call(name string) *Call { |
|
|
|
|
const ctx = "function call" |
|
|
|
|
desiredArgs := 1 |
|
|
|
|
if ret.Op.isAggregatorWithParam() { |
|
|
|
|
desiredArgs = 2 |
|
|
|
|
|
|
|
|
|
fn, exist := getFunction(name) |
|
|
|
|
if !exist { |
|
|
|
|
p.errorf("unknown function with name %q", name) |
|
|
|
|
ret.Param = arguments[0] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
p.expect(LEFT_PAREN, ctx) |
|
|
|
|
// Might be call without args.
|
|
|
|
|
if p.peek().Typ == RIGHT_PAREN { |
|
|
|
|
p.next() // Consume.
|
|
|
|
|
return &Call{fn, nil} |
|
|
|
|
if len(arguments) != desiredArgs { |
|
|
|
|
p.errorf("wrong number of arguments for aggregate expression provided, expected %d, got %d", desiredArgs, len(arguments)) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var args []Expr |
|
|
|
|
for { |
|
|
|
|
e := p.expr() |
|
|
|
|
args = append(args, e) |
|
|
|
|
ret.Expr = arguments[desiredArgs-1] |
|
|
|
|
|
|
|
|
|
// 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} |
|
|
|
|
return ret |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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) |
|
|
|
|
// number parses a number.
|
|
|
|
|
func (p *parser) number(val string) float64 { |
|
|
|
|
n, err := strconv.ParseInt(val, 0, 64) |
|
|
|
|
f := float64(n) |
|
|
|
|
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 != "" { |
|
|
|
|
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) |
|
|
|
|
if err != nil { |
|
|
|
|
panic(err) // Must not happen with metric.Equal.
|
|
|
|
|
} |
|
|
|
|
ret.LabelMatchers = append(ret.LabelMatchers, m) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
for _, lm := range ret.LabelMatchers { |
|
|
|
|
if !lm.Matches("") { |
|
|
|
|
notEmpty = true |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
f, err = strconv.ParseFloat(val, 64) |
|
|
|
|
} |
|
|
|
|
if !notEmpty { |
|
|
|
|
p.errorf("vector selector must contain at least one non-empty matcher") |
|
|
|
|
if err != nil { |
|
|
|
|
p.errorf("error parsing number: %s", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ret |
|
|
|
|
return f |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// expectType checks the type of the node and raises an error if it
|
|
|
|
|
@ -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 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|