From af3277f8326431808ecec2a2095404ad3422a929 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 3 Dec 2025 18:46:35 +0100 Subject: [PATCH 1/8] PromQL: Add `fill*()` binop modifiers to provide default values for missing series Signed-off-by: Julius Volz --- promql/engine.go | 54 +- promql/parser/ast.go | 13 + promql/parser/generated_parser.y | 73 +- promql/parser/generated_parser.y.go | 1284 +++++++++-------- promql/parser/lex.go | 31 + promql/parser/parse.go | 6 + promql/parser/printer.go | 13 + promql/parser/printer_test.go | 20 + web/api/v1/translate_ast.go | 4 + .../ExplainViews/BinaryExpr/VectorVector.tsx | 73 +- web/ui/mantine-ui/src/promql/ast.ts | 5 + web/ui/mantine-ui/src/promql/binOp.test.ts | 24 + web/ui/mantine-ui/src/promql/binOp.ts | 29 +- web/ui/mantine-ui/src/promql/format.tsx | 43 +- web/ui/mantine-ui/src/promql/serialize.ts | 18 +- .../src/promql/serializeAndFormat.test.ts | 13 + .../src/complete/promql.terms.ts | 4 + .../src/parser/vector.test.ts | 85 +- .../codemirror-promql/src/parser/vector.ts | 35 + .../codemirror-promql/src/types/vector.ts | 7 + web/ui/module/lezer-promql/src/promql.grammar | 24 +- web/ui/module/lezer-promql/src/tokens.js | 140 +- 22 files changed, 1296 insertions(+), 702 deletions(-) diff --git a/promql/engine.go b/promql/engine.go index 57a1f41bb8..b609dc4f0a 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -2862,7 +2862,8 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * if matching.Card == parser.CardManyToMany { panic("many-to-many only allowed for set operators") } - if len(lhs) == 0 || len(rhs) == 0 { + if (len(lhs) == 0 && len(rhs) == 0) || + ((len(lhs) == 0 || len(rhs) == 0) && matching.FillValues.RHS == nil && matching.FillValues.LHS == nil) { return nil, nil // Short-circuit: nothing is going to match. } @@ -2910,17 +2911,9 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * } matchedSigs := enh.matchedSigs - // For all lhs samples find a respective rhs sample and perform - // the binary operation. var lastErr error - for i, ls := range lhs { - sigOrd := lhsh[i].sigOrdinal - - rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector. - if !found { - continue - } + doBinOp := func(ls, rs Sample, sigOrd int) { // Account for potentially swapped sidedness. fl, fr := ls.F, rs.F hl, hr := ls.H, rs.H @@ -2931,7 +2924,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * floatValue, histogramValue, keep, info, err := vectorElemBinop(op, fl, fr, hl, hr, pos) if err != nil { lastErr = err - continue + return } if info != nil { lastErr = info @@ -2971,7 +2964,7 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * } if !keep && !returnBool { - continue + return } enh.Out = append(enh.Out, Sample{ @@ -2981,6 +2974,43 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * DropName: returnBool, }) } + + // For all lhs samples, find a respective rhs sample and perform + // the binary operation. + for i, ls := range lhs { + sigOrd := lhsh[i].sigOrdinal + + rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector. + if !found { + fill := matching.FillValues.RHS + if fill == nil { + continue + } + rs = Sample{ + Metric: ls.Metric.MatchLabels(matching.On, matching.MatchingLabels...), + F: *fill, + } + } + + doBinOp(ls, rs, sigOrd) + } + + // For any rhs samples which have not been matched, check if we need to + // perform the operation with a fill value from the lhs. + if fill := matching.FillValues.LHS; fill != nil { + for sigOrd, rs := range rightSigs { + if _, matched := matchedSigs[sigOrd]; matched { + continue // Already matched. + } + ls := Sample{ + Metric: rs.Metric.MatchLabels(matching.On, matching.MatchingLabels...), + F: *fill, + } + + doBinOp(ls, rs, sigOrd) + } + } + return enh.Out, lastErr } diff --git a/promql/parser/ast.go b/promql/parser/ast.go index 130f9aefb7..6496095287 100644 --- a/promql/parser/ast.go +++ b/promql/parser/ast.go @@ -318,6 +318,19 @@ type VectorMatching struct { // Include contains additional labels that should be included in // the result from the side with the lower cardinality. Include []string + // Fill-in values to use when a series from one side does not find a match on the other side. + FillValues VectorMatchFillValues +} + +// VectorMatchFillValues contains the fill values to use for Vector matching +// when one side does not find a match on the other side. +// When a fill value is nil, no fill is applied for that side, and there +// is no output for the match group if there is no match. +type VectorMatchFillValues struct { + // RHS is the fill value to use for the right-hand side. + RHS *float64 + // LHS is the fill value to use for the left-hand side. + LHS *float64 } // Visitor allows visiting a Node and its child nodes. The Visit method is diff --git a/promql/parser/generated_parser.y b/promql/parser/generated_parser.y index 47776f53d0..71ab6ed4b3 100644 --- a/promql/parser/generated_parser.y +++ b/promql/parser/generated_parser.y @@ -139,6 +139,9 @@ BOOL BY GROUP_LEFT GROUP_RIGHT +FILL +FILL_LEFT +FILL_RIGHT IGNORING OFFSET SMOOTHED @@ -190,7 +193,7 @@ START_METRIC_SELECTOR %type int %type uint %type number series_value signed_number signed_or_unsigned_number -%type step_invariant_expr aggregate_expr aggregate_modifier bin_modifier binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers label_matchers matrix_selector number_duration_literal offset_expr anchored_expr smoothed_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector duration_expr paren_duration_expr positive_duration_expr offset_duration_expr +%type step_invariant_expr aggregate_expr aggregate_modifier bin_modifier fill_modifiers binary_expr bool_modifier expr function_call function_call_args function_call_body group_modifiers fill_value label_matchers matrix_selector number_duration_literal offset_expr anchored_expr smoothed_expr on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector duration_expr paren_duration_expr positive_duration_expr offset_duration_expr %start start @@ -302,7 +305,7 @@ binary_expr : expr ADD bin_modifier expr { $$ = yylex.(*parser).newBinar // Using left recursion for the modifier rules, helps to keep the parser stack small and // reduces allocations. -bin_modifier : group_modifiers; +bin_modifier : fill_modifiers; bool_modifier : /* empty */ { $$ = &BinaryExpr{ @@ -346,6 +349,47 @@ group_modifiers: bool_modifier /* empty */ } ; +fill_modifiers: group_modifiers /* empty */ + /* Only fill() */ + | group_modifiers FILL fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + /* Only fill_left() */ + | group_modifiers FILL_LEFT fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + } + /* Only fill_right() */ + | group_modifiers FILL_RIGHT fill_value + { + $$ = $1 + fill := $3.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + /* fill_left() fill_right() */ + | group_modifiers FILL_LEFT fill_value FILL_RIGHT fill_value + { + $$ = $1 + fill_left := $3.(*NumberLiteral).Val + fill_right := $5.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + /* fill_right() fill_left() */ + | group_modifiers FILL_RIGHT fill_value FILL_LEFT fill_value + { + fill_right := $3.(*NumberLiteral).Val + fill_left := $5.(*NumberLiteral).Val + $$.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + $$.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + ; grouping_labels : LEFT_PAREN grouping_label_list RIGHT_PAREN { $$ = $2 } @@ -387,6 +431,21 @@ grouping_label : maybe_label { yylex.(*parser).unexpected("grouping opts", "label"); $$ = Item{} } ; +fill_value : LEFT_PAREN number_duration_literal RIGHT_PAREN + { + $$ = $2.(*NumberLiteral) + } + | LEFT_PAREN unary_op number_duration_literal RIGHT_PAREN + { + nl := $3.(*NumberLiteral) + if $2.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = $2.Pos + $$ = nl + } + ; + /* * Function calls. */ @@ -697,7 +756,7 @@ metric : metric_identifier label_set ; -metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; +metric_identifier: AVG | BOTTOMK | BY | COUNT | COUNT_VALUES | FILL | FILL_LEFT | FILL_RIGHT | GROUP | IDENTIFIER | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | QUANTILE | STDDEV | STDVAR | SUM | TOPK | WITHOUT | START | END | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; label_set : LEFT_BRACE label_set_list RIGHT_BRACE { $$ = labels.New($2...) } @@ -954,7 +1013,7 @@ counter_reset_hint : UNKNOWN_COUNTER_RESET | COUNTER_RESET | NOT_COUNTER_RESET | aggregate_op : AVG | BOTTOMK | COUNT | COUNT_VALUES | GROUP | MAX | MIN | QUANTILE | STDDEV | STDVAR | SUM | TOPK | LIMITK | LIMIT_RATIO; // Inside of grouping options label names can be recognized as keywords by the lexer. This is a list of keywords that could also be a label name. -maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; +maybe_label : AVG | BOOL | BOTTOMK | BY | COUNT | COUNT_VALUES | GROUP | GROUP_LEFT | GROUP_RIGHT | FILL | FILL_LEFT | FILL_RIGHT | IDENTIFIER | IGNORING | LAND | LOR | LUNLESS | MAX | METRIC_IDENTIFIER | MIN | OFFSET | ON | QUANTILE | STDDEV | STDVAR | SUM | TOPK | START | END | ATAN2 | LIMITK | LIMIT_RATIO | STEP | RANGE | ANCHORED | SMOOTHED; unary_op : ADD | SUB; @@ -1162,7 +1221,7 @@ offset_duration_expr : number_duration_literal } | duration_expr ; - + min_max: MIN | MAX ; duration_expr : number_duration_literal @@ -1277,14 +1336,14 @@ duration_expr : number_duration_literal ; paren_duration_expr : LEFT_PAREN duration_expr RIGHT_PAREN - { + { yylex.(*parser).experimentalDurationExpr($2.(Expr)) if durationExpr, ok := $2.(*DurationExpr); ok { durationExpr.Wrapped = true $$ = durationExpr break } - $$ = $2 + $$ = $2 } ; diff --git a/promql/parser/generated_parser.y.go b/promql/parser/generated_parser.y.go index f5feec0b55..d20460ed5b 100644 --- a/promql/parser/generated_parser.y.go +++ b/promql/parser/generated_parser.y.go @@ -113,31 +113,34 @@ const BOOL = 57420 const BY = 57421 const GROUP_LEFT = 57422 const GROUP_RIGHT = 57423 -const IGNORING = 57424 -const OFFSET = 57425 -const SMOOTHED = 57426 -const ANCHORED = 57427 -const ON = 57428 -const WITHOUT = 57429 -const keywordsEnd = 57430 -const preprocessorStart = 57431 -const START = 57432 -const END = 57433 -const STEP = 57434 -const RANGE = 57435 -const preprocessorEnd = 57436 -const counterResetHintsStart = 57437 -const UNKNOWN_COUNTER_RESET = 57438 -const COUNTER_RESET = 57439 -const NOT_COUNTER_RESET = 57440 -const GAUGE_TYPE = 57441 -const counterResetHintsEnd = 57442 -const startSymbolsStart = 57443 -const START_METRIC = 57444 -const START_SERIES_DESCRIPTION = 57445 -const START_EXPRESSION = 57446 -const START_METRIC_SELECTOR = 57447 -const startSymbolsEnd = 57448 +const FILL = 57424 +const FILL_LEFT = 57425 +const FILL_RIGHT = 57426 +const IGNORING = 57427 +const OFFSET = 57428 +const SMOOTHED = 57429 +const ANCHORED = 57430 +const ON = 57431 +const WITHOUT = 57432 +const keywordsEnd = 57433 +const preprocessorStart = 57434 +const START = 57435 +const END = 57436 +const STEP = 57437 +const RANGE = 57438 +const preprocessorEnd = 57439 +const counterResetHintsStart = 57440 +const UNKNOWN_COUNTER_RESET = 57441 +const COUNTER_RESET = 57442 +const NOT_COUNTER_RESET = 57443 +const GAUGE_TYPE = 57444 +const counterResetHintsEnd = 57445 +const startSymbolsStart = 57446 +const START_METRIC = 57447 +const START_SERIES_DESCRIPTION = 57448 +const START_EXPRESSION = 57449 +const START_METRIC_SELECTOR = 57450 +const startSymbolsEnd = 57451 var yyToknames = [...]string{ "$end", @@ -221,6 +224,9 @@ var yyToknames = [...]string{ "BY", "GROUP_LEFT", "GROUP_RIGHT", + "FILL", + "FILL_LEFT", + "FILL_RIGHT", "IGNORING", "OFFSET", "SMOOTHED", @@ -258,376 +264,403 @@ var yyExca = [...]int16{ -1, 1, 1, -1, -2, 0, - -1, 41, - 1, 150, - 10, 150, - 24, 150, + -1, 44, + 1, 161, + 10, 161, + 24, 161, -2, 0, - -1, 72, - 2, 193, - 15, 193, - 79, 193, - 87, 193, - -2, 107, - -1, 73, - 2, 194, - 15, 194, - 79, 194, - 87, 194, - -2, 108, - -1, 74, - 2, 195, - 15, 195, - 79, 195, - 87, 195, - -2, 110, -1, 75, - 2, 196, - 15, 196, - 79, 196, - 87, 196, - -2, 111, - -1, 76, - 2, 197, - 15, 197, - 79, 197, - 87, 197, - -2, 112, - -1, 77, - 2, 198, - 15, 198, - 79, 198, - 87, 198, - -2, 117, - -1, 78, - 2, 199, - 15, 199, - 79, 199, - 87, 199, - -2, 119, - -1, 79, - 2, 200, - 15, 200, - 79, 200, - 87, 200, - -2, 121, - -1, 80, - 2, 201, - 15, 201, - 79, 201, - 87, 201, - -2, 122, - -1, 81, - 2, 202, - 15, 202, - 79, 202, - 87, 202, - -2, 123, - -1, 82, - 2, 203, - 15, 203, - 79, 203, - 87, 203, - -2, 124, - -1, 83, 2, 204, 15, 204, 79, 204, - 87, 204, - -2, 125, - -1, 84, + 90, 204, + -2, 115, + -1, 76, 2, 205, 15, 205, 79, 205, - 87, 205, - -2, 129, - -1, 85, + 90, 205, + -2, 116, + -1, 77, 2, 206, 15, 206, 79, 206, - 87, 206, + 90, 206, + -2, 118, + -1, 78, + 2, 207, + 15, 207, + 79, 207, + 90, 207, + -2, 119, + -1, 79, + 2, 208, + 15, 208, + 79, 208, + 90, 208, + -2, 123, + -1, 80, + 2, 209, + 15, 209, + 79, 209, + 90, 209, + -2, 128, + -1, 81, + 2, 210, + 15, 210, + 79, 210, + 90, 210, -2, 130, - -1, 137, - 41, 274, - 42, 274, - 52, 274, - 53, 274, - 57, 274, + -1, 82, + 2, 211, + 15, 211, + 79, 211, + 90, 211, + -2, 132, + -1, 83, + 2, 212, + 15, 212, + 79, 212, + 90, 212, + -2, 133, + -1, 84, + 2, 213, + 15, 213, + 79, 213, + 90, 213, + -2, 134, + -1, 85, + 2, 214, + 15, 214, + 79, 214, + 90, 214, + -2, 135, + -1, 86, + 2, 215, + 15, 215, + 79, 215, + 90, 215, + -2, 136, + -1, 87, + 2, 216, + 15, 216, + 79, 216, + 90, 216, + -2, 140, + -1, 88, + 2, 217, + 15, 217, + 79, 217, + 90, 217, + -2, 141, + -1, 140, + 41, 288, + 42, 288, + 52, 288, + 53, 288, + 57, 288, -2, 22, - -1, 251, - 9, 259, - 12, 259, - 13, 259, - 18, 259, - 19, 259, - 25, 259, - 41, 259, - 47, 259, - 48, 259, - 51, 259, - 57, 259, - 62, 259, - 63, 259, - 64, 259, - 65, 259, - 66, 259, - 67, 259, - 68, 259, - 69, 259, - 70, 259, - 71, 259, - 72, 259, - 73, 259, - 74, 259, - 75, 259, - 79, 259, - 83, 259, - 84, 259, - 85, 259, - 87, 259, - 90, 259, - 91, 259, - 92, 259, - 93, 259, + -1, 258, + 9, 273, + 12, 273, + 13, 273, + 18, 273, + 19, 273, + 25, 273, + 41, 273, + 47, 273, + 48, 273, + 51, 273, + 57, 273, + 62, 273, + 63, 273, + 64, 273, + 65, 273, + 66, 273, + 67, 273, + 68, 273, + 69, 273, + 70, 273, + 71, 273, + 72, 273, + 73, 273, + 74, 273, + 75, 273, + 79, 273, + 82, 273, + 83, 273, + 84, 273, + 86, 273, + 87, 273, + 88, 273, + 90, 273, + 93, 273, + 94, 273, + 95, 273, + 96, 273, -2, 0, - -1, 252, - 9, 259, - 12, 259, - 13, 259, - 18, 259, - 19, 259, - 25, 259, - 41, 259, - 47, 259, - 48, 259, - 51, 259, - 57, 259, - 62, 259, - 63, 259, - 64, 259, - 65, 259, - 66, 259, - 67, 259, - 68, 259, - 69, 259, - 70, 259, - 71, 259, - 72, 259, - 73, 259, - 74, 259, - 75, 259, - 79, 259, - 83, 259, - 84, 259, - 85, 259, - 87, 259, - 90, 259, - 91, 259, - 92, 259, - 93, 259, + -1, 259, + 9, 273, + 12, 273, + 13, 273, + 18, 273, + 19, 273, + 25, 273, + 41, 273, + 47, 273, + 48, 273, + 51, 273, + 57, 273, + 62, 273, + 63, 273, + 64, 273, + 65, 273, + 66, 273, + 67, 273, + 68, 273, + 69, 273, + 70, 273, + 71, 273, + 72, 273, + 73, 273, + 74, 273, + 75, 273, + 79, 273, + 82, 273, + 83, 273, + 84, 273, + 86, 273, + 87, 273, + 88, 273, + 90, 273, + 93, 273, + 94, 273, + 95, 273, + 96, 273, -2, 0, } const yyPrivate = 57344 -const yyLast = 1050 +const yyLast = 1224 var yyAct = [...]int16{ - 58, 186, 413, 411, 341, 418, 286, 243, 197, 95, - 189, 48, 355, 144, 70, 227, 93, 251, 252, 356, - 159, 190, 65, 120, 17, 88, 127, 130, 128, 129, - 22, 425, 426, 427, 428, 131, 249, 121, 124, 335, - 250, 67, 132, 126, 408, 407, 377, 332, 125, 123, - 331, 102, 126, 122, 336, 154, 324, 6, 397, 18, - 19, 111, 112, 20, 135, 114, 137, 119, 101, 375, - 337, 323, 375, 330, 11, 12, 14, 15, 16, 21, - 23, 25, 26, 27, 28, 29, 33, 34, 43, 133, - 329, 13, 116, 118, 117, 24, 38, 37, 146, 30, - 402, 124, 31, 32, 35, 36, 130, 412, 138, 396, - 194, 125, 123, 328, 131, 126, 365, 182, 239, 401, - 193, 199, 204, 205, 206, 207, 208, 209, 177, 363, - 362, 181, 200, 200, 200, 200, 200, 200, 200, 178, - 120, 238, 223, 201, 201, 201, 201, 201, 201, 201, - 212, 215, 134, 200, 136, 211, 210, 2, 3, 4, - 5, 222, 233, 221, 201, 245, 235, 384, 333, 371, - 228, 247, 229, 360, 370, 359, 246, 358, 188, 273, - 140, 368, 114, 195, 119, 194, 277, 139, 62, 369, - 268, 237, 229, 271, 185, 193, 441, 200, 61, 196, - 367, 201, 273, 383, 155, 278, 279, 280, 201, 116, - 118, 117, 231, 200, 236, 121, 124, 195, 382, 440, - 86, 218, 230, 232, 201, 381, 125, 123, 276, 275, - 126, 122, 231, 196, 274, 146, 87, 132, 439, 327, - 429, 438, 230, 232, 248, 141, 184, 183, 419, 253, - 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, - 264, 265, 266, 267, 334, 357, 191, 192, 214, 353, - 354, 202, 203, 361, 121, 124, 88, 364, 283, 7, - 39, 213, 282, 199, 200, 125, 123, 395, 200, 126, - 122, 366, 10, 194, 200, 201, 394, 281, 393, 201, - 392, 391, 90, 193, 390, 201, 160, 161, 162, 163, - 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, - 174, 389, 194, 388, 120, 195, 373, 387, 386, 385, - 153, 99, 193, 62, 442, 374, 376, 200, 378, 185, - 56, 196, 40, 61, 379, 380, 89, 152, 201, 151, - 1, 100, 102, 103, 195, 104, 105, 175, 71, 108, - 109, 398, 111, 112, 113, 86, 114, 115, 119, 101, - 196, 66, 200, 55, 9, 9, 54, 404, 8, 53, - 406, 87, 41, 201, 52, 158, 410, 51, 414, 415, - 416, 184, 183, 116, 118, 117, 421, 420, 423, 422, - 417, 430, 50, 49, 289, 47, 156, 216, 147, 46, - 431, 432, 200, 372, 299, 433, 202, 203, 145, 96, - 305, 435, 157, 201, 403, 437, 326, 288, 147, 94, - 436, 97, 45, 44, 57, 242, 434, 234, 145, 338, - 443, 200, 97, 98, 121, 124, 143, 240, 284, 301, - 302, 97, 201, 303, 91, 125, 123, 424, 187, 126, - 122, 316, 287, 59, 290, 292, 294, 295, 296, 304, - 306, 309, 310, 311, 312, 313, 317, 318, 142, 0, - 291, 293, 297, 298, 300, 307, 322, 321, 308, 289, - 96, 0, 314, 315, 319, 320, 226, 150, 405, 299, - 94, 225, 149, 0, 0, 305, 0, 0, 92, 285, - 0, 0, 288, 97, 224, 148, 62, 121, 124, 0, - 0, 0, 272, 0, 0, 0, 61, 0, 125, 123, - 0, 0, 126, 122, 301, 302, 0, 0, 303, 0, - 0, 0, 0, 0, 0, 0, 316, 0, 86, 290, - 292, 294, 295, 296, 304, 306, 309, 310, 311, 312, - 313, 317, 318, 0, 87, 291, 293, 297, 298, 300, - 307, 322, 321, 308, 184, 183, 0, 314, 315, 319, - 320, 62, 0, 120, 60, 88, 0, 63, 0, 0, - 22, 61, 0, 0, 217, 0, 0, 64, 0, 269, - 270, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 100, 102, 0, 86, 0, 0, 0, 0, 0, 18, - 19, 111, 112, 20, 0, 114, 115, 119, 101, 87, - 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 0, 0, - 400, 13, 116, 118, 117, 24, 38, 37, 399, 30, - 0, 0, 31, 32, 68, 69, 62, 42, 0, 60, - 88, 0, 63, 0, 0, 22, 61, 121, 124, 0, - 0, 0, 64, 0, 121, 124, 0, 0, 125, 123, - 0, 0, 126, 122, 0, 125, 123, 0, 86, 126, - 122, 0, 0, 0, 18, 19, 0, 0, 20, 0, - 0, 0, 0, 0, 87, 0, 0, 0, 0, 72, - 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 0, 0, 0, 13, 0, 0, 220, - 24, 38, 37, 0, 30, 0, 325, 31, 32, 68, - 69, 62, 0, 0, 60, 88, 0, 63, 121, 124, - 22, 61, 0, 0, 0, 0, 0, 64, 0, 125, - 123, 0, 0, 126, 122, 0, 0, 0, 0, 0, - 121, 124, 0, 86, 0, 0, 0, 0, 0, 18, - 19, 125, 123, 20, 0, 126, 122, 0, 0, 87, - 0, 0, 0, 0, 72, 73, 74, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 17, 39, - 0, 13, 0, 0, 22, 24, 38, 37, 0, 30, - 340, 0, 31, 32, 68, 69, 0, 339, 0, 0, - 0, 343, 344, 342, 349, 351, 348, 350, 345, 346, - 347, 352, 241, 18, 19, 0, 194, 20, 0, 244, - 0, 0, 0, 247, 0, 0, 193, 0, 11, 12, - 14, 15, 16, 21, 23, 25, 26, 27, 28, 29, - 33, 34, 0, 0, 120, 13, 0, 0, 195, 24, - 38, 37, 219, 30, 0, 0, 31, 32, 35, 36, - 0, 0, 0, 120, 196, 0, 0, 0, 0, 0, - 0, 100, 102, 103, 0, 104, 105, 106, 107, 108, - 109, 110, 111, 112, 113, 0, 114, 115, 119, 101, - 100, 102, 103, 0, 104, 105, 106, 107, 108, 109, - 110, 111, 112, 113, 198, 114, 115, 119, 101, 120, - 0, 62, 0, 116, 118, 117, 0, 185, 176, 0, - 0, 61, 0, 0, 0, 62, 0, 0, 0, 0, - 0, 185, 116, 118, 117, 61, 100, 102, 103, 0, - 104, 105, 106, 86, 108, 109, 110, 111, 112, 113, - 0, 114, 115, 119, 101, 0, 0, 86, 0, 87, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 184, - 183, 0, 0, 87, 0, 0, 0, 0, 116, 118, - 117, 0, 0, 184, 183, 409, 0, 0, 0, 0, - 0, 0, 0, 0, 202, 203, 343, 344, 342, 349, - 351, 348, 350, 345, 346, 347, 352, 0, 179, 180, + 61, 363, 190, 429, 351, 436, 431, 293, 247, 201, + 98, 51, 147, 193, 369, 96, 231, 412, 413, 370, + 132, 133, 68, 130, 73, 163, 194, 131, 443, 444, + 445, 446, 134, 135, 256, 253, 254, 255, 257, 258, + 259, 129, 70, 426, 123, 425, 124, 127, 391, 342, + 157, 458, 223, 198, 447, 389, 415, 128, 126, 345, + 451, 129, 125, 197, 414, 465, 398, 138, 379, 140, + 6, 103, 105, 106, 346, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 199, 117, 118, 122, 104, + 347, 136, 343, 46, 124, 127, 389, 133, 334, 251, + 397, 200, 149, 377, 192, 128, 126, 199, 134, 129, + 125, 198, 141, 333, 420, 396, 119, 121, 120, 123, + 186, 197, 395, 200, 203, 208, 209, 210, 211, 212, + 213, 181, 376, 419, 430, 204, 204, 204, 204, 204, + 204, 204, 182, 199, 185, 227, 205, 205, 205, 205, + 205, 205, 205, 216, 219, 215, 204, 341, 214, 200, + 137, 117, 139, 122, 339, 385, 237, 205, 239, 464, + 384, 249, 226, 2, 3, 4, 5, 91, 290, 225, + 340, 123, 289, 280, 250, 383, 364, 338, 124, 127, + 284, 119, 121, 120, 275, 195, 196, 288, 218, 128, + 126, 204, 460, 129, 125, 205, 280, 278, 158, 105, + 374, 217, 205, 286, 287, 423, 243, 204, 241, 114, + 115, 124, 127, 117, 373, 122, 104, 372, 205, 222, + 143, 437, 128, 126, 124, 127, 129, 125, 65, 242, + 149, 240, 337, 142, 42, 128, 126, 418, 64, 129, + 125, 285, 252, 119, 121, 120, 365, 366, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 344, 371, 127, 367, 368, 198, 283, + 375, 124, 127, 282, 378, 128, 126, 281, 197, 129, + 203, 204, 128, 126, 135, 204, 129, 125, 198, 380, + 65, 204, 205, 144, 7, 409, 205, 408, 197, 407, + 64, 406, 205, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 202, 232, + 199, 233, 89, 156, 417, 65, 387, 405, 463, 233, + 404, 189, 102, 224, 403, 64, 200, 204, 90, 388, + 390, 10, 392, 124, 127, 393, 394, 462, 205, 402, + 461, 93, 124, 127, 128, 126, 401, 89, 129, 125, + 400, 235, 399, 128, 126, 416, 410, 129, 125, 235, + 8, 234, 236, 90, 44, 59, 204, 411, 43, 234, + 236, 92, 422, 188, 187, 1, 179, 205, 424, 155, + 428, 154, 230, 432, 433, 434, 150, 229, 74, 335, + 439, 438, 441, 440, 449, 450, 148, 435, 58, 452, + 228, 206, 207, 448, 336, 57, 296, 56, 386, 100, + 204, 69, 453, 454, 9, 9, 309, 455, 99, 55, + 457, 205, 315, 124, 127, 162, 421, 150, 97, 295, + 99, 54, 459, 53, 128, 126, 238, 148, 129, 125, + 97, 100, 153, 204, 466, 146, 52, 152, 95, 50, + 100, 311, 312, 100, 205, 313, 160, 220, 49, 161, + 151, 48, 159, 326, 47, 60, 297, 299, 301, 302, + 303, 314, 316, 319, 320, 321, 322, 323, 327, 328, + 246, 456, 298, 300, 304, 305, 306, 307, 308, 310, + 317, 332, 331, 318, 296, 348, 101, 324, 325, 329, + 330, 245, 244, 291, 309, 198, 94, 442, 248, 191, + 315, 350, 251, 294, 292, 197, 62, 295, 349, 145, + 0, 0, 353, 354, 352, 359, 361, 358, 360, 355, + 356, 357, 362, 0, 0, 0, 0, 199, 0, 311, + 312, 0, 0, 313, 0, 0, 0, 0, 0, 0, + 0, 326, 0, 200, 297, 299, 301, 302, 303, 314, + 316, 319, 320, 321, 322, 323, 327, 328, 0, 0, + 298, 300, 304, 305, 306, 307, 308, 310, 317, 332, + 331, 318, 0, 0, 0, 324, 325, 329, 330, 65, + 0, 0, 63, 91, 0, 66, 427, 0, 25, 64, + 0, 0, 221, 0, 0, 67, 0, 353, 354, 352, + 359, 361, 358, 360, 355, 356, 357, 362, 0, 0, + 0, 89, 0, 0, 0, 0, 0, 21, 22, 0, + 0, 23, 0, 0, 0, 0, 0, 90, 0, 0, + 0, 0, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 0, 0, 0, 13, + 0, 0, 16, 17, 18, 0, 27, 41, 40, 0, + 33, 0, 0, 34, 35, 71, 72, 65, 45, 0, + 63, 91, 0, 66, 0, 0, 25, 64, 0, 0, + 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, + 0, 0, 0, 0, 0, 21, 22, 0, 0, 23, + 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 0, 0, 0, 13, 0, 0, + 16, 17, 18, 0, 27, 41, 40, 0, 33, 0, + 0, 34, 35, 71, 72, 65, 0, 0, 63, 91, + 0, 66, 0, 0, 25, 64, 0, 0, 0, 0, + 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 89, 0, 0, + 0, 0, 0, 21, 22, 0, 0, 23, 0, 0, + 0, 0, 0, 90, 0, 0, 0, 0, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 0, 0, 0, 13, 0, 0, 16, 17, + 18, 0, 27, 41, 40, 0, 33, 20, 91, 34, + 35, 71, 72, 25, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 21, 22, 0, 0, 23, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 11, 12, 14, + 15, 19, 24, 26, 28, 29, 30, 31, 32, 36, + 37, 0, 0, 0, 13, 0, 0, 16, 17, 18, + 0, 27, 41, 40, 0, 33, 20, 42, 34, 35, + 38, 39, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 21, 22, 0, 0, 23, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 12, 14, 15, + 19, 24, 26, 28, 29, 30, 31, 32, 36, 37, + 123, 0, 0, 13, 0, 0, 16, 17, 18, 0, + 27, 41, 40, 0, 33, 0, 0, 34, 35, 38, + 39, 123, 0, 0, 0, 0, 0, 103, 105, 106, + 0, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 0, 117, 118, 122, 104, 0, 0, 103, 105, + 106, 0, 107, 108, 109, 0, 111, 112, 113, 114, + 115, 116, 382, 117, 118, 122, 104, 0, 0, 65, + 0, 123, 119, 121, 120, 189, 65, 0, 0, 64, + 0, 381, 189, 0, 0, 0, 64, 0, 0, 0, + 0, 0, 0, 119, 121, 120, 0, 0, 103, 105, + 106, 89, 107, 108, 0, 0, 111, 112, 89, 114, + 115, 116, 180, 117, 118, 122, 104, 90, 0, 65, + 0, 0, 0, 0, 90, 189, 65, 188, 187, 64, + 0, 0, 279, 0, 188, 187, 64, 123, 0, 0, + 0, 0, 0, 119, 121, 120, 0, 0, 0, 0, + 0, 89, 0, 0, 0, 206, 207, 0, 89, 0, + 0, 0, 206, 207, 103, 105, 0, 90, 0, 0, + 0, 0, 0, 0, 90, 114, 115, 188, 187, 117, + 118, 122, 104, 0, 188, 187, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 183, 184, 0, 0, 119, + 121, 120, 276, 277, } var yyPact = [...]int16{ - 55, 269, 806, 806, 657, 12, -1000, -1000, -1000, 267, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 488, - -1000, 329, -1000, 889, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -4, 27, - 222, -1000, -1000, 742, -1000, 742, 263, -1000, 172, 165, - 230, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 426, -1000, - -1000, 495, -1000, -1000, 345, 326, -1000, -1000, 31, -1000, - -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, - -58, -58, -58, -58, -58, -58, 956, -1000, -1000, 176, - 942, 324, 324, 324, 324, 324, 324, 222, -52, -1000, - 266, 266, 572, -1000, 870, 717, 126, -13, -1000, 141, - 139, 324, 494, -1000, -1000, 168, 188, -1000, -1000, 417, - -1000, 189, -1000, 116, 847, 742, -1000, -46, -63, -1000, - 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, - 742, 742, 742, 742, 742, -1000, -1000, -1000, 507, 219, - 214, 213, -4, -1000, -1000, 324, -1000, 190, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 101, 101, 276, -1000, -4, - -1000, 324, 172, 165, 59, 59, -13, -13, -13, -13, - -1000, -1000, -1000, 487, -1000, -1000, 49, -1000, 889, -1000, - -1000, -1000, -1000, 739, -1000, 406, -1000, 88, -1000, -1000, - -1000, -1000, -1000, 48, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, 21, 142, 13, -1000, -1000, -1000, 813, 9, 266, - 266, 266, 266, 126, 126, 569, 569, 569, 310, 935, - 569, 569, 310, 126, 126, 569, 126, 9, -1000, 162, - 160, 158, 324, -13, 108, 107, 324, 717, 94, -1000, - -1000, -1000, 179, -1000, 167, -1000, -1000, -1000, -1000, -1000, + 68, 294, 934, 934, 688, 855, -1000, -1000, -1000, 231, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 742, 324, -1000, -1000, -1000, -1000, - -1000, -1000, 53, 53, 20, 53, 155, 155, 201, 150, - -1000, -1000, 323, 322, 321, 317, 315, 298, 295, 294, - 292, 290, 281, -1000, -1000, -1000, -1000, -1000, 87, 36, - 324, 636, -1000, -1000, 643, -1000, 98, -1000, -1000, -1000, - 402, -1000, 889, 476, -1000, -1000, -1000, 53, -1000, 19, - 18, 1008, -1000, -1000, -1000, 50, 284, 284, 284, 101, - 234, 234, 50, 234, 50, -65, -1000, -1000, 233, -1000, - 324, -1000, -1000, -1000, -1000, -1000, -1000, 53, 53, -1000, - -1000, -1000, 53, -1000, -1000, -1000, -1000, -1000, -1000, 284, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 324, - 403, -1000, -1000, -1000, 217, -1000, 174, -1000, 313, -1000, - -1000, -1000, -1000, -1000, + -1000, -1000, 448, -1000, 340, -1000, 996, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 5, 18, 279, -1000, -1000, 776, -1000, 776, 164, + -1000, 228, 215, 288, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, 445, -1000, -1000, 460, -1000, -1000, 397, 329, -1000, + -1000, 26, -1000, -53, -53, -53, -53, -53, -53, -53, + -53, -53, -53, -53, -53, -53, -53, -53, -53, 1120, + -1000, -1000, 102, 326, 1077, 1077, 1077, 1077, 1077, 1077, + 279, -58, -1000, 196, 196, 600, -1000, 30, 321, 105, + -15, -1000, 157, 150, 1077, 400, -1000, -1000, 327, 335, + -1000, -1000, 436, -1000, 216, -1000, 214, 516, 776, -1000, + -47, -51, -41, -1000, 776, 776, 776, 776, 776, 776, + 776, 776, 776, 776, 776, 776, 776, 776, 776, -1000, + -1000, -1000, 1127, 272, 268, 264, 5, -1000, -1000, 1077, + -1000, 236, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 269, + 269, 176, -1000, 5, -1000, 1077, 228, 215, 233, 233, + -15, -15, -15, -15, -1000, -1000, -1000, 512, -1000, -1000, + 91, -1000, 996, -1000, -1000, -1000, -1000, 402, -1000, 404, + -1000, 162, -1000, -1000, -1000, -1000, -1000, 155, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, 23, 66, 33, -1000, -1000, + -1000, 514, 167, 171, 171, 171, 196, 196, 196, 196, + 105, 105, 1133, 1133, 1133, 1067, 1017, 1133, 1133, 1067, + 105, 105, 1133, 105, 167, -1000, 212, 209, 195, 1077, + -15, 110, 81, 1077, 321, 46, -1000, -1000, -1000, 1070, + -1000, 163, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 776, 1077, -1000, -1000, -1000, -1000, + -1000, -1000, 36, 36, 22, 36, 83, 83, 98, 49, + -1000, -1000, 366, 364, 360, 353, 338, 334, 331, 305, + 303, 301, 299, -1000, 291, -67, -65, -1000, -1000, -1000, + -1000, -1000, 42, 34, 1077, 312, -1000, -1000, 240, -1000, + 112, -1000, -1000, -1000, 424, -1000, 996, 193, -1000, -1000, + -1000, 36, -1000, 19, 17, 599, -1000, -1000, -1000, 77, + 289, 289, 289, 269, 217, 217, 77, 217, 77, -71, + 32, 229, 171, 171, -1000, -1000, 53, -1000, 1077, -1000, + -1000, -1000, -1000, -1000, -1000, 36, 36, -1000, -1000, -1000, + 36, -1000, -1000, -1000, -1000, -1000, -1000, 289, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 29, -1000, + -1000, 1077, 180, -1000, -1000, -1000, 336, -1000, -1000, 147, + -1000, 44, -1000, -1000, -1000, -1000, -1000, } var yyPgo = [...]int16{ - 0, 478, 13, 463, 6, 15, 462, 371, 22, 458, - 9, 457, 14, 292, 378, 454, 16, 448, 19, 12, - 447, 443, 7, 439, 4, 5, 436, 3, 2, 10, - 435, 21, 1, 434, 433, 26, 204, 432, 422, 88, - 409, 407, 28, 406, 41, 405, 11, 403, 402, 387, - 385, 384, 379, 376, 373, 340, 0, 358, 8, 357, - 350, 342, + 0, 539, 12, 536, 7, 16, 533, 431, 22, 529, + 10, 527, 24, 351, 380, 526, 15, 523, 19, 14, + 522, 516, 8, 515, 4, 5, 501, 3, 6, 13, + 500, 26, 2, 485, 484, 23, 208, 482, 481, 479, + 93, 478, 477, 27, 476, 1, 42, 469, 11, 466, + 453, 451, 445, 439, 427, 425, 418, 385, 0, 408, + 9, 396, 395, 388, } var yyR1 = [...]int8{ - 0, 60, 60, 60, 60, 60, 60, 60, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 34, 34, 34, 34, 35, 35, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, - 37, 37, 37, 37, 37, 36, 38, 38, 50, 50, - 43, 43, 43, 43, 18, 18, 18, 18, 17, 17, - 17, 4, 4, 4, 40, 42, 42, 41, 41, 41, - 51, 58, 47, 47, 48, 49, 33, 33, 33, 9, - 9, 45, 53, 53, 53, 53, 53, 53, 54, 55, - 55, 55, 44, 44, 44, 1, 1, 1, 2, 2, - 2, 2, 2, 2, 2, 14, 14, 7, 7, 7, + 0, 62, 62, 62, 62, 62, 62, 62, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 34, 34, 34, 34, 35, 35, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 36, 39, 39, 52, 52, + 44, 44, 44, 44, 37, 37, 37, 37, 37, 37, + 18, 18, 18, 18, 17, 17, 17, 4, 4, 4, + 45, 45, 41, 43, 43, 42, 42, 42, 53, 60, + 49, 49, 50, 51, 33, 33, 33, 9, 9, 47, + 55, 55, 55, 55, 55, 55, 56, 57, 57, 57, + 46, 46, 46, 1, 1, 1, 2, 2, 2, 2, + 2, 2, 2, 14, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 13, 13, 13, 13, 15, - 15, 15, 16, 16, 16, 16, 16, 16, 16, 61, - 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 30, 30, 30, 22, 22, 22, 22, - 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 25, 25, 26, 26, 26, 11, - 11, 11, 11, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 13, 13, 13, 13, + 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, + 63, 21, 21, 21, 21, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 30, 30, 30, 22, 22, 22, + 22, 23, 23, 23, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 25, 25, 26, 26, 26, + 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 8, 8, 5, 5, 5, 5, 46, 46, 29, 29, - 31, 31, 32, 32, 28, 27, 27, 52, 10, 19, - 19, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 12, 12, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 57, + 6, 6, 6, 6, 8, 8, 5, 5, 5, 5, + 48, 48, 29, 29, 31, 31, 32, 32, 28, 27, + 27, 54, 10, 19, 19, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 12, 12, 58, 58, 58, + 58, 58, 58, 58, 58, 58, 58, 58, 58, 59, } var yyR2 = [...]int8{ @@ -636,126 +669,131 @@ var yyR2 = [...]int8{ 1, 1, 1, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 0, 1, 3, 3, - 1, 1, 3, 3, 3, 4, 2, 1, 3, 1, - 2, 1, 1, 1, 2, 3, 2, 3, 1, 2, - 3, 1, 3, 3, 2, 2, 3, 5, 3, 1, - 1, 4, 6, 5, 6, 5, 4, 3, 2, 2, - 1, 1, 3, 4, 2, 3, 1, 2, 3, 3, - 1, 3, 3, 2, 1, 2, 1, 1, 1, 1, + 1, 1, 3, 3, 1, 3, 3, 3, 5, 5, + 3, 4, 2, 1, 3, 1, 2, 1, 1, 1, + 3, 4, 2, 3, 2, 3, 1, 2, 3, 1, + 3, 3, 2, 2, 3, 5, 3, 1, 1, 4, + 6, 5, 6, 5, 4, 3, 2, 2, 1, 1, + 3, 4, 2, 3, 1, 2, 3, 3, 1, 3, + 3, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 3, 4, 2, 0, 3, - 1, 2, 3, 3, 1, 3, 3, 2, 1, 2, - 0, 3, 2, 1, 1, 3, 1, 3, 4, 1, - 3, 5, 5, 1, 1, 1, 4, 3, 3, 2, - 3, 1, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 4, 3, 3, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 3, 4, 2, 0, + 3, 1, 2, 3, 3, 1, 3, 3, 2, 1, + 2, 0, 3, 2, 1, 1, 3, 1, 3, 4, + 1, 3, 5, 5, 1, 1, 1, 4, 3, 3, + 2, 3, 1, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 3, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 1, 1, 1, 2, 1, 1, 1, 0, - 1, 1, 2, 3, 3, 4, 4, 6, 7, 4, - 1, 1, 1, 1, 2, 3, 3, 3, 3, 3, - 3, 3, 3, 6, 1, 3, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 2, + 1, 1, 1, 0, 1, 1, 2, 3, 3, 4, + 4, 6, 7, 4, 1, 1, 1, 1, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 6, 1, 3, } var yyChk = [...]int16{ - -1000, -60, 102, 103, 104, 105, 2, 10, -14, -7, - -13, 62, 63, 79, 64, 65, 66, 12, 47, 48, - 51, 67, 18, 68, 83, 69, 70, 71, 72, 73, - 87, 90, 91, 74, 75, 92, 93, 85, 84, 13, - -61, -14, 10, -39, -34, -37, -40, -45, -46, -47, - -48, -49, -51, -52, -53, -54, -55, -33, -56, -3, - 12, 19, 9, 15, 25, -8, -7, -44, 92, 93, - -12, -57, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 75, 41, 57, 13, -55, - -13, -15, 20, -16, 12, -10, 2, 25, -21, 2, - 41, 59, 42, 43, 45, 46, 47, 48, 49, 50, - 51, 52, 53, 54, 56, 57, 83, 85, 84, 58, - 14, 41, 57, 53, 42, 52, 56, -35, -42, 2, - 79, 87, 15, -42, -39, -56, -39, -56, -44, 15, - 15, 15, -1, 20, -2, 12, -10, 2, 20, 7, - 2, 4, 2, 4, 24, -36, -43, -38, -50, 78, - -36, -36, -36, -36, -36, -36, -36, -36, -36, -36, - -36, -36, -36, -36, -36, -59, 2, -46, -8, 92, - 93, -12, -56, 68, 67, 15, -32, -9, 2, -29, - -31, 90, 91, 19, 9, 41, 57, -58, 2, -56, - -46, -8, 92, 93, -56, -56, -56, -56, -56, -56, - -42, -35, -18, 15, 2, -18, -41, 22, -39, 22, - 22, 22, 22, -56, 20, 7, 2, -5, 2, 4, - 54, 44, 55, -5, 20, -16, 25, 2, 25, 2, - -20, 5, -30, -22, 12, -29, -31, 16, -39, 82, - 86, 80, 81, -39, -39, -39, -39, -39, -39, -39, - -39, -39, -39, -39, -39, -39, -39, -39, -46, 92, - 93, -12, 15, -56, 15, 15, 15, -56, 15, -29, - -29, 21, 6, 2, -17, 22, -4, -6, 25, 2, - 62, 78, 63, 79, 64, 65, 66, 80, 81, 12, - 82, 47, 48, 51, 67, 18, 68, 83, 86, 69, - 70, 71, 72, 73, 90, 91, 59, 74, 75, 92, - 93, 85, 84, 22, 7, 7, 20, -2, 25, 2, + -1000, -62, 105, 106, 107, 108, 2, 10, -14, -7, + -13, 62, 63, 79, 64, 65, 82, 83, 84, 66, + 12, 47, 48, 51, 67, 18, 68, 86, 69, 70, + 71, 72, 73, 90, 93, 94, 74, 75, 95, 96, + 88, 87, 13, -63, -14, 10, -40, -34, -38, -41, + -47, -48, -49, -50, -51, -53, -54, -55, -56, -57, + -33, -58, -3, 12, 19, 9, 15, 25, -8, -7, + -46, 95, 96, -12, -59, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 41, + 57, 13, -57, -13, -15, 20, -16, 12, -10, 2, + 25, -21, 2, 41, 59, 42, 43, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 56, 57, 86, + 88, 87, 58, 14, 41, 57, 53, 42, 52, 56, + -35, -43, 2, 79, 90, 15, -43, -40, -58, -40, + -58, -46, 15, 15, 15, -1, 20, -2, 12, -10, + 2, 20, 7, 2, 4, 2, 4, 24, -36, -37, + -44, -39, -52, 78, -36, -36, -36, -36, -36, -36, + -36, -36, -36, -36, -36, -36, -36, -36, -36, -61, + 2, -48, -8, 95, 96, -12, -58, 68, 67, 15, + -32, -9, 2, -29, -31, 93, 94, 19, 9, 41, + 57, -60, 2, -58, -48, -8, 95, 96, -58, -58, + -58, -58, -58, -58, -43, -35, -18, 15, 2, -18, + -42, 22, -40, 22, 22, 22, 22, -58, 20, 7, + 2, -5, 2, 4, 54, 44, 55, -5, 20, -16, + 25, 2, 25, 2, -20, 5, -30, -22, 12, -29, + -31, 16, -40, 82, 83, 84, 85, 89, 80, 81, + -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, + -40, -40, -40, -40, -40, -48, 95, 96, -12, 15, + -58, 15, 15, 15, -58, 15, -29, -29, 21, 6, + 2, -17, 22, -4, -6, 25, 2, 62, 78, 63, + 79, 64, 65, 66, 80, 81, 82, 83, 84, 12, + 85, 47, 48, 51, 67, 18, 68, 86, 89, 69, + 70, 71, 72, 73, 93, 94, 59, 74, 75, 95, + 96, 88, 87, 22, 7, 7, 20, -2, 25, 2, 25, 2, 26, 26, -31, 26, 41, 57, -23, 24, 17, -24, 30, 28, 29, 35, 36, 37, 33, 31, - 34, 32, 38, -18, -18, -19, -18, -19, 15, 15, - 15, -56, 22, 22, -56, 22, -58, 21, 2, 22, - 7, 2, -39, -56, -28, 19, -28, 26, -28, -22, - -22, 24, 17, 2, 17, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 22, 22, -56, 22, - 7, 21, 2, 22, -4, 22, -28, 26, 26, 17, - -24, -27, 57, -28, -32, -32, -32, -29, -25, 14, - -25, -27, -25, -27, -11, 96, 97, 98, 99, 7, - -56, -28, -28, -28, -26, -32, -56, 22, 24, 21, - 2, 22, 21, -32, + 34, 32, 38, -45, 15, -45, -45, -18, -18, -19, + -18, -19, 15, 15, 15, -58, 22, 22, -58, 22, + -60, 21, 2, 22, 7, 2, -40, -58, -28, 19, + -28, 26, -28, -22, -22, 24, 17, 2, 17, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + -48, -8, 84, 83, 22, 22, -58, 22, 7, 21, + 2, 22, -4, 22, -28, 26, 26, 17, -24, -27, + 57, -28, -32, -32, -32, -29, -25, 14, -25, -27, + -25, -27, -11, 99, 100, 101, 102, 22, -48, -45, + -45, 7, -58, -28, -28, -28, -26, -32, 22, -58, + 22, 24, 21, 2, 22, 21, -32, } var yyDef = [...]int16{ - 0, -2, 138, 138, 0, 0, 7, 6, 1, 138, - 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, - 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, - 126, 127, 128, 129, 130, 131, 132, 133, 134, 0, - 2, -2, 3, 4, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, - 113, 246, 247, 0, 257, 0, 90, 91, 131, 132, - 0, 284, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, -2, 240, 241, 0, 5, - 105, 0, 137, 140, 0, 144, 148, 258, 149, 153, - 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, - 46, 46, 46, 46, 46, 46, 0, 74, 75, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 25, 26, - 0, 0, 0, 64, 0, 22, 88, -2, 89, 0, - 0, 0, 0, 94, 96, 0, 100, 104, 135, 0, - 141, 0, 147, 0, 152, 0, 45, 50, 51, 47, + 0, -2, 149, 149, 0, 0, 7, 6, 1, 149, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 0, 2, -2, 3, 4, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 0, 124, 260, 261, 0, 271, 0, 98, + 99, 142, 143, 0, 298, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, 254, + 255, 0, 5, 113, 0, 148, 151, 0, 155, 159, + 272, 160, 164, 46, 46, 46, 46, 46, 46, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 0, + 82, 83, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 25, 26, 0, 0, 0, 72, 0, 22, 96, + -2, 97, 0, 0, 0, 0, 102, 104, 0, 108, + 112, 146, 0, 152, 0, 158, 0, 163, 0, 45, + 54, 50, 51, 47, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, + 81, 275, 0, 0, 0, 0, 284, 285, 286, 0, + 84, 0, 86, 266, 267, 87, 88, 262, 263, 0, + 0, 0, 95, 79, 287, 0, 0, 0, 289, 290, + 291, 292, 293, 294, 23, 24, 27, 0, 63, 28, + 0, 74, 76, 78, 299, 295, 296, 0, 100, 0, + 105, 0, 111, 256, 257, 258, 259, 0, 147, 150, + 153, 156, 154, 157, 162, 165, 167, 170, 174, 175, + 176, 0, 29, 0, 0, 0, 0, 0, -2, -2, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 276, 0, 0, 0, 0, + 288, 0, 0, 0, 0, 0, 264, 265, 89, 0, + 94, 0, 62, 65, 67, 68, 69, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 73, 77, 0, 101, 103, 106, 110, + 107, 109, 0, 0, 0, 0, 0, 0, 0, 0, + 180, 182, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 55, 0, 56, 57, 48, 49, 52, + 274, 53, 0, 0, 0, 0, 277, 278, 0, 85, + 0, 91, 93, 60, 0, 66, 75, 0, 166, 268, + 168, 0, 171, 0, 0, 0, 178, 183, 179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 72, 73, 261, 0, 0, - 0, 0, 270, 271, 272, 0, 76, 0, 78, 252, - 253, 79, 80, 248, 249, 0, 0, 0, 87, 71, - 273, 0, 0, 0, 275, 276, 277, 278, 279, 280, - 23, 24, 27, 0, 57, 28, 0, 66, 68, 70, - 285, 281, 282, 0, 92, 0, 97, 0, 103, 242, - 243, 244, 245, 0, 136, 139, 142, 145, 143, 146, - 151, 154, 156, 159, 163, 164, 165, 0, 29, 0, - 0, -2, -2, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 262, 0, - 0, 0, 0, 274, 0, 0, 0, 0, 0, 250, - 251, 81, 0, 86, 0, 56, 59, 61, 62, 63, - 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, - 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, - 237, 238, 239, 65, 69, 0, 93, 95, 98, 102, - 99, 101, 0, 0, 0, 0, 0, 0, 0, 0, - 169, 171, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 48, 49, 52, 260, 53, 0, 0, - 0, 0, 263, 264, 0, 77, 0, 83, 85, 54, - 0, 60, 67, 0, 155, 254, 157, 0, 160, 0, - 0, 0, 167, 172, 168, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 265, 266, 0, 269, - 0, 82, 84, 55, 58, 283, 158, 0, 0, 166, - 170, 173, 0, 256, 174, 175, 176, 177, 178, 0, - 179, 180, 181, 182, 183, 189, 190, 191, 192, 0, - 0, 161, 162, 255, 0, 187, 0, 267, 0, 185, - 188, 268, 184, 186, + 0, 0, 0, 0, 279, 280, 0, 283, 0, 90, + 92, 61, 64, 297, 169, 0, 0, 177, 181, 184, + 0, 270, 185, 186, 187, 188, 189, 0, 190, 191, + 192, 193, 194, 200, 201, 202, 203, 70, 0, 58, + 59, 0, 0, 172, 173, 269, 0, 198, 71, 0, + 281, 0, 196, 199, 282, 195, 197, } var yyTok1 = [...]int8{ @@ -773,7 +811,7 @@ var yyTok2 = [...]int8{ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, - 102, 103, 104, 105, 106, + 102, 103, 104, 105, 106, 107, 108, 109, } var yyTok3 = [...]int8{ @@ -1298,44 +1336,83 @@ yydefault: yyVAL.node.(*BinaryExpr).VectorMatching.Card = CardOneToMany yyVAL.node.(*BinaryExpr).VectorMatching.Include = yyDollar[3].strings } - case 54: + case 55: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + case 56: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill + } + case 57: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill := yyDollar[3].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill + } + case 58: + yyDollar = yyS[yypt-5 : yypt+1] + { + yyVAL.node = yyDollar[1].node + fill_left := yyDollar[3].node.(*NumberLiteral).Val + fill_right := yyDollar[5].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + case 59: + yyDollar = yyS[yypt-5 : yypt+1] + { + fill_right := yyDollar[3].node.(*NumberLiteral).Val + fill_left := yyDollar[5].node.(*NumberLiteral).Val + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.LHS = &fill_left + yyVAL.node.(*BinaryExpr).VectorMatching.FillValues.RHS = &fill_right + } + case 60: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.strings = yyDollar[2].strings } - case 55: + case 61: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.strings = yyDollar[2].strings } - case 56: + case 62: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.strings = []string{} } - case 57: + case 63: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "\"(\"") yyVAL.strings = nil } - case 58: + case 64: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.strings = append(yyDollar[1].strings, yyDollar[3].item.Val) } - case 59: + case 65: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.strings = []string{yyDollar[1].item.Val} } - case 60: + case 66: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "\",\" or \")\"") yyVAL.strings = yyDollar[1].strings } - case 61: + case 67: yyDollar = yyS[yypt-1 : yypt+1] { if !model.UTF8Validation.IsValidLabelName(yyDollar[1].item.Val) { @@ -1343,7 +1420,7 @@ yydefault: } yyVAL.item = yyDollar[1].item } - case 62: + case 68: yyDollar = yyS[yypt-1 : yypt+1] { unquoted := yylex.(*parser).unquoteString(yyDollar[1].item.Val) @@ -1354,13 +1431,28 @@ yydefault: yyVAL.item.Pos++ yyVAL.item.Val = unquoted } - case 63: + case 69: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("grouping opts", "label") yyVAL.item = Item{} } - case 64: + case 70: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[2].node.(*NumberLiteral) + } + case 71: + yyDollar = yyS[yypt-4 : yypt+1] + { + nl := yyDollar[3].node.(*NumberLiteral) + if yyDollar[2].item.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = yyDollar[2].item.Pos + yyVAL.node = nl + } + case 72: yyDollar = yyS[yypt-2 : yypt+1] { fn, exist := getFunction(yyDollar[1].item.Val, yylex.(*parser).functions) @@ -1379,38 +1471,38 @@ yydefault: }, } } - case 65: + case 73: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = yyDollar[2].node } - case 66: + case 74: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.node = Expressions{} } - case 67: + case 75: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = append(yyDollar[1].node.(Expressions), yyDollar[3].node.(Expr)) } - case 68: + case 76: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = Expressions{yyDollar[1].node.(Expr)} } - case 69: + case 77: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).addParseErrf(yyDollar[2].item.PositionRange(), "trailing commas not allowed in function call args") yyVAL.node = yyDollar[1].node } - case 70: + case 78: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &ParenExpr{Expr: yyDollar[2].node.(Expr), PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item)} } - case 71: + case 79: yyDollar = yyS[yypt-1 : yypt+1] { if numLit, ok := yyDollar[1].node.(*NumberLiteral); ok { @@ -1424,7 +1516,7 @@ yydefault: } yyVAL.node = yyDollar[1].node } - case 72: + case 80: yyDollar = yyS[yypt-3 : yypt+1] { if numLit, ok := yyDollar[3].node.(*NumberLiteral); ok { @@ -1435,41 +1527,41 @@ yydefault: yylex.(*parser).addOffsetExpr(yyDollar[1].node, yyDollar[3].node.(*DurationExpr)) yyVAL.node = yyDollar[1].node } - case 73: + case 81: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("offset", "number, duration, step(), or range()") yyVAL.node = yyDollar[1].node } - case 74: + case 82: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).setAnchored(yyDollar[1].node) } - case 75: + case 83: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).setSmoothed(yyDollar[1].node) } - case 76: + case 84: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).setTimestamp(yyDollar[1].node, yyDollar[3].float) yyVAL.node = yyDollar[1].node } - case 77: + case 85: yyDollar = yyS[yypt-5 : yypt+1] { yylex.(*parser).setAtModifierPreprocessor(yyDollar[1].node, yyDollar[3].item) yyVAL.node = yyDollar[1].node } - case 78: + case 86: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("@", "timestamp") yyVAL.node = yyDollar[1].node } - case 81: + case 89: yyDollar = yyS[yypt-4 : yypt+1] { var errMsg string @@ -1499,7 +1591,7 @@ yydefault: EndPos: yylex.(*parser).lastClosing, } } - case 82: + case 90: yyDollar = yyS[yypt-6 : yypt+1] { var rangeNl time.Duration @@ -1521,7 +1613,7 @@ yydefault: EndPos: yyDollar[6].item.Pos + 1, } } - case 83: + case 91: yyDollar = yyS[yypt-5 : yypt+1] { var rangeNl time.Duration @@ -1536,31 +1628,31 @@ yydefault: EndPos: yyDollar[5].item.Pos + 1, } } - case 84: + case 92: yyDollar = yyS[yypt-6 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "\"]\"") yyVAL.node = yyDollar[1].node } - case 85: + case 93: yyDollar = yyS[yypt-5 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "number, duration, step(), range(), or \"]\"") yyVAL.node = yyDollar[1].node } - case 86: + case 94: yyDollar = yyS[yypt-4 : yypt+1] { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") yyVAL.node = yyDollar[1].node } - case 87: + case 95: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("subquery or range selector", "number, duration, step(), or range()") yyVAL.node = yyDollar[1].node } - case 88: + case 96: yyDollar = yyS[yypt-2 : yypt+1] { if nl, ok := yyDollar[2].node.(*NumberLiteral); ok { @@ -1573,7 +1665,7 @@ yydefault: yyVAL.node = &UnaryExpr{Op: yyDollar[1].item.Typ, Expr: yyDollar[2].node.(Expr), StartPos: yyDollar[1].item.Pos} } } - case 89: + case 97: yyDollar = yyS[yypt-2 : yypt+1] { vs := yyDollar[2].node.(*VectorSelector) @@ -1582,7 +1674,7 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 90: + case 98: yyDollar = yyS[yypt-1 : yypt+1] { vs := &VectorSelector{ @@ -1593,14 +1685,14 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 91: + case 99: yyDollar = yyS[yypt-1 : yypt+1] { vs := yyDollar[1].node.(*VectorSelector) yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 92: + case 100: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1608,7 +1700,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item), } } - case 93: + case 101: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1616,7 +1708,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[4].item), } } - case 94: + case 102: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1624,7 +1716,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[2].item), } } - case 95: + case 103: yyDollar = yyS[yypt-3 : yypt+1] { if yyDollar[1].matchers != nil { @@ -1633,144 +1725,144 @@ yydefault: yyVAL.matchers = yyDollar[1].matchers } } - case 96: + case 104: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matchers = []*labels.Matcher{yyDollar[1].matcher} } - case 97: + case 105: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") yyVAL.matchers = yyDollar[1].matchers } - case 98: + case 106: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } - case 99: + case 107: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } - case 100: + case 108: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matcher = yylex.(*parser).newMetricNameMatcher(yyDollar[1].item) } - case 101: + case 109: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label matching", "string") yyVAL.matcher = nil } - case 102: + case 110: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label matching", "string") yyVAL.matcher = nil } - case 103: + case 111: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "label matching operator") yyVAL.matcher = nil } - case 104: + case 112: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label matching", "identifier or \"}\"") yyVAL.matcher = nil } - case 105: + case 113: yyDollar = yyS[yypt-2 : yypt+1] { b := labels.NewBuilder(yyDollar[2].labels) b.Set(labels.MetricName, yyDollar[1].item.Val) yyVAL.labels = b.Labels() } - case 106: + case 114: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.labels = yyDollar[1].labels } - case 135: + case 146: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.labels = labels.New(yyDollar[2].lblList...) } - case 136: + case 147: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.labels = labels.New(yyDollar[2].lblList...) } - case 137: + case 148: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.labels = labels.New() } - case 138: + case 149: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.labels = labels.New() } - case 139: + case 150: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.lblList = append(yyDollar[1].lblList, yyDollar[3].label) } - case 140: + case 151: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.lblList = []labels.Label{yyDollar[1].label} } - case 141: + case 152: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\",\" or \"}\"") yyVAL.lblList = yyDollar[1].lblList } - case 142: + case 153: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } - case 143: + case 154: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } - case 144: + case 155: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.label = labels.Label{Name: labels.MetricName, Value: yyDollar[1].item.Val} } - case 145: + case 156: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label set", "string") yyVAL.label = labels.Label{} } - case 146: + case 157: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("label set", "string") yyVAL.label = labels.Label{} } - case 147: + case 158: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\"=\"") yyVAL.label = labels.Label{} } - case 148: + case 159: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label set", "identifier or \"}\"") yyVAL.label = labels.Label{} } - case 149: + case 160: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).generatedParserResult = &seriesDescription{ @@ -1778,33 +1870,33 @@ yydefault: values: yyDollar[2].series, } } - case 150: + case 161: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.series = []SequenceValue{} } - case 151: + case 162: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = append(yyDollar[1].series, yyDollar[3].series...) } - case 152: + case 163: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.series = yyDollar[1].series } - case 153: + case 164: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("series values", "") yyVAL.series = nil } - case 154: + case 165: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Omitted: true}} } - case 155: + case 166: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1812,12 +1904,12 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Omitted: true}) } } - case 156: + case 167: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Value: yyDollar[1].float}} } - case 157: + case 168: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1826,7 +1918,7 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Value: yyDollar[1].float}) } } - case 158: + case 169: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1836,12 +1928,12 @@ yydefault: yyDollar[1].float += yyDollar[2].float } } - case 159: + case 170: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Histogram: yyDollar[1].histogram}} } - case 160: + case 171: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1851,7 +1943,7 @@ yydefault: //$1 += $2 } } - case 161: + case 172: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsIncreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1860,7 +1952,7 @@ yydefault: } yyVAL.series = val } - case 162: + case 173: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsDecreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1869,7 +1961,7 @@ yydefault: } yyVAL.series = val } - case 163: + case 174: yyDollar = yyS[yypt-1 : yypt+1] { if yyDollar[1].item.Val != "stale" { @@ -1877,130 +1969,130 @@ yydefault: } yyVAL.float = math.Float64frombits(value.StaleNaN) } - case 166: + case 177: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } - case 167: + case 178: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } - case 168: + case 179: yyDollar = yyS[yypt-3 : yypt+1] { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } - case 169: + case 180: yyDollar = yyS[yypt-2 : yypt+1] { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } - case 170: + case 181: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = *(yylex.(*parser).mergeMaps(&yyDollar[1].descriptors, &yyDollar[3].descriptors)) } - case 171: + case 182: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.descriptors = yyDollar[1].descriptors } - case 172: + case 183: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("histogram description", "histogram description key, e.g. buckets:[5 10 7]") } - case 173: + case 184: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["schema"] = yyDollar[3].int } - case 174: + case 185: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["sum"] = yyDollar[3].float } - case 175: + case 186: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["count"] = yyDollar[3].float } - case 176: + case 187: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket"] = yyDollar[3].float } - case 177: + case 188: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["z_bucket_w"] = yyDollar[3].float } - case 178: + case 189: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["custom_values"] = yyDollar[3].bucket_set } - case 179: + case 190: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["buckets"] = yyDollar[3].bucket_set } - case 180: + case 191: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["offset"] = yyDollar[3].int } - case 181: + case 192: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_buckets"] = yyDollar[3].bucket_set } - case 182: + case 193: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["n_offset"] = yyDollar[3].int } - case 183: + case 194: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() yyVAL.descriptors["counter_reset_hint"] = yyDollar[3].item } - case 184: + case 195: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.bucket_set = yyDollar[2].bucket_set } - case 185: + case 196: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.bucket_set = yyDollar[2].bucket_set } - case 186: + case 197: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.bucket_set = append(yyDollar[1].bucket_set, yyDollar[3].float) } - case 187: + case 198: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.bucket_set = []float64{yyDollar[1].float} } - case 246: + case 260: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &NumberLiteral{ @@ -2008,7 +2100,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 247: + case 261: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2023,12 +2115,12 @@ yydefault: Duration: true, } } - case 248: + case 262: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.float = yylex.(*parser).number(yyDollar[1].item.Val) } - case 249: + case 263: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2039,17 +2131,17 @@ yydefault: } yyVAL.float = dur.Seconds() } - case 250: + case 264: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = yyDollar[2].float } - case 251: + case 265: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = -yyDollar[2].float } - case 254: + case 268: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -2058,17 +2150,17 @@ yydefault: yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "invalid repetition in series values: %s", err) } } - case 255: + case 269: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.int = -int64(yyDollar[2].uint) } - case 256: + case 270: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.int = int64(yyDollar[1].uint) } - case 257: + case 271: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &StringLiteral{ @@ -2076,7 +2168,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 258: + case 272: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.item = Item{ @@ -2085,12 +2177,12 @@ yydefault: Val: yylex.(*parser).unquoteString(yyDollar[1].item.Val), } } - case 259: + case 273: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.strings = nil } - case 261: + case 275: yyDollar = yyS[yypt-1 : yypt+1] { nl := yyDollar[1].node.(*NumberLiteral) @@ -2101,7 +2193,7 @@ yydefault: } yyVAL.node = nl } - case 262: + case 276: yyDollar = yyS[yypt-2 : yypt+1] { nl := yyDollar[2].node.(*NumberLiteral) @@ -2116,7 +2208,7 @@ yydefault: nl.PosRange.Start = yyDollar[1].item.Pos yyVAL.node = nl } - case 263: + case 277: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2125,7 +2217,7 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 264: + case 278: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2134,7 +2226,7 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 265: + case 279: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2147,7 +2239,7 @@ yydefault: StartPos: yyDollar[1].item.Pos, } } - case 266: + case 280: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2160,7 +2252,7 @@ yydefault: StartPos: yyDollar[1].item.Pos, } } - case 267: + case 281: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2171,7 +2263,7 @@ yydefault: RHS: yyDollar[5].node.(Expr), } } - case 268: + case 282: yyDollar = yyS[yypt-7 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2187,7 +2279,7 @@ yydefault: }, } } - case 269: + case 283: yyDollar = yyS[yypt-4 : yypt+1] { de := yyDollar[3].node.(*DurationExpr) @@ -2202,7 +2294,7 @@ yydefault: } yyVAL.node = yyDollar[3].node } - case 273: + case 287: yyDollar = yyS[yypt-1 : yypt+1] { nl := yyDollar[1].node.(*NumberLiteral) @@ -2213,7 +2305,7 @@ yydefault: } yyVAL.node = nl } - case 274: + case 288: yyDollar = yyS[yypt-2 : yypt+1] { switch expr := yyDollar[2].node.(type) { @@ -2246,25 +2338,25 @@ yydefault: break } } - case 275: + case 289: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: ADD, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 276: + case 290: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: SUB, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 277: + case 291: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: MUL, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 278: + case 292: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) @@ -2275,7 +2367,7 @@ yydefault: } yyVAL.node = &DurationExpr{Op: DIV, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 279: + case 293: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) @@ -2286,13 +2378,13 @@ yydefault: } yyVAL.node = &DurationExpr{Op: MOD, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 280: + case 294: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[1].node.(Expr)) yyVAL.node = &DurationExpr{Op: POW, LHS: yyDollar[1].node.(Expr), RHS: yyDollar[3].node.(Expr)} } - case 281: + case 295: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2301,7 +2393,7 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 282: + case 296: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2310,7 +2402,7 @@ yydefault: EndPos: yyDollar[3].item.PositionRange().End, } } - case 283: + case 297: yyDollar = yyS[yypt-6 : yypt+1] { yyVAL.node = &DurationExpr{ @@ -2321,7 +2413,7 @@ yydefault: RHS: yyDollar[5].node.(Expr), } } - case 285: + case 299: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).experimentalDurationExpr(yyDollar[2].node.(Expr)) diff --git a/promql/parser/lex.go b/promql/parser/lex.go index b3a82dc0c6..7149985767 100644 --- a/promql/parser/lex.go +++ b/promql/parser/lex.go @@ -137,6 +137,9 @@ var key = map[string]ItemType{ "ignoring": IGNORING, "group_left": GROUP_LEFT, "group_right": GROUP_RIGHT, + "fill": FILL, + "fill_left": FILL_LEFT, + "fill_right": FILL_RIGHT, "bool": BOOL, // Preprocessors. @@ -1083,6 +1086,17 @@ Loop: word := l.input[l.start:l.pos] switch kw, ok := key[strings.ToLower(word)]; { case ok: + // For fill/fill_left/fill_right, only treat as keyword if followed by '(' + // This allows using these as metric names (e.g., "fill + fill"). + // This could be done for other keywords as well, but for the new fill + // modifiers this is especially important so we don't break any existing + // queries. + if kw == FILL || kw == FILL_LEFT || kw == FILL_RIGHT { + if !l.peekFollowedByLeftParen() { + l.emit(IDENTIFIER) + break Loop + } + } l.emit(kw) case !strings.Contains(word, ":"): l.emit(IDENTIFIER) @@ -1098,6 +1112,23 @@ Loop: return lexStatements } +// peekFollowedByLeftParen checks if the next non-whitespace character is '('. +// This is used for context-sensitive keywords like fill/fill_left/fill_right +// that should only be treated as keywords when followed by '('. +func (l *Lexer) peekFollowedByLeftParen() bool { + pos := l.pos + for { + if int(pos) >= len(l.input) { + return false + } + r, w := utf8.DecodeRuneInString(l.input[pos:]) + if !isSpace(r) { + return r == '(' + } + pos += posrange.Pos(w) + } +} + func isSpace(r rune) bool { return r == ' ' || r == '\t' || r == '\n' || r == '\r' } diff --git a/promql/parser/parse.go b/promql/parser/parse.go index 817e0d02d9..a872706364 100644 --- a/promql/parser/parse.go +++ b/promql/parser/parse.go @@ -768,6 +768,9 @@ func (p *parser) checkAST(node Node) (typ ValueType) { if len(n.VectorMatching.MatchingLabels) > 0 { p.addParseErrf(n.PositionRange(), "vector matching only allowed between instant vectors") } + if n.VectorMatching.FillValues.LHS != nil || n.VectorMatching.FillValues.RHS != nil { + p.addParseErrf(n.PositionRange(), "filling in missing series only allowed between instant vectors") + } n.VectorMatching = nil case n.Op.IsSetOperator(): // Both operands are Vectors. if n.VectorMatching.Card == CardOneToMany || n.VectorMatching.Card == CardManyToOne { @@ -776,6 +779,9 @@ func (p *parser) checkAST(node Node) (typ ValueType) { if n.VectorMatching.Card != CardManyToMany { p.addParseErrf(n.PositionRange(), "set operations must always be many-to-many") } + if n.VectorMatching.FillValues.LHS != nil || n.VectorMatching.FillValues.RHS != nil { + p.addParseErrf(n.PositionRange(), "filling in missing series not allowed for set operators") + } } if (lt == ValueTypeScalar || rt == ValueTypeScalar) && n.Op.IsSetOperator() { diff --git a/promql/parser/printer.go b/promql/parser/printer.go index 01e2c46c1b..44ca15e532 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -172,6 +172,19 @@ func (node *BinaryExpr) getMatchingStr() string { b.WriteString(")") matching += b.String() } + + if vm.FillValues.LHS != nil || vm.FillValues.RHS != nil { + if vm.FillValues.LHS == vm.FillValues.RHS { + matching += fmt.Sprintf(" fill (%v)", *vm.FillValues.LHS) + } else { + if vm.FillValues.LHS != nil { + matching += fmt.Sprintf(" fill_left (%v)", *vm.FillValues.LHS) + } + if vm.FillValues.RHS != nil { + matching += fmt.Sprintf(" fill_right (%v)", *vm.FillValues.RHS) + } + } + } } return matching } diff --git a/promql/parser/printer_test.go b/promql/parser/printer_test.go index 4499fa7860..a5f254527e 100644 --- a/promql/parser/printer_test.go +++ b/promql/parser/printer_test.go @@ -113,6 +113,26 @@ func TestExprString(t *testing.T) { in: `a - ignoring() group_left c`, out: `a - ignoring () group_left () c`, }, + { + in: `a + fill(-23) b`, + out: `a + fill (-23) b`, + }, + { + in: `a + fill_left(-23) b`, + out: `a + fill_left (-23) b`, + }, + { + in: `a + fill_right(42) b`, + out: `a + fill_right (42) b`, + }, + { + in: `a + fill_left(-23) fill_right(42) b`, + out: `a + fill_left (-23) fill_right (42) b`, + }, + { + in: `a + on(b) group_left fill(-23) c`, + out: `a + on (b) group_left () fill (-23) c`, + }, { in: `up > bool 0`, }, diff --git a/web/api/v1/translate_ast.go b/web/api/v1/translate_ast.go index 3cce0583f9..3c2bc09943 100644 --- a/web/api/v1/translate_ast.go +++ b/web/api/v1/translate_ast.go @@ -47,6 +47,10 @@ func translateAST(node parser.Expr) any { "labels": sanitizeList(m.MatchingLabels), "on": m.On, "include": sanitizeList(m.Include), + "fillValues": map[string]*float64{ + "lhs": m.FillValues.LHS, + "rhs": m.FillValues.RHS, + }, } } diff --git a/web/ui/mantine-ui/src/pages/query/ExplainViews/BinaryExpr/VectorVector.tsx b/web/ui/mantine-ui/src/pages/query/ExplainViews/BinaryExpr/VectorVector.tsx index e70b7a3f3e..5c10357561 100644 --- a/web/ui/mantine-ui/src/pages/query/ExplainViews/BinaryExpr/VectorVector.tsx +++ b/web/ui/mantine-ui/src/pages/query/ExplainViews/BinaryExpr/VectorVector.tsx @@ -8,6 +8,7 @@ import { MatchErrorType, computeVectorVectorBinOp, filteredSampleValue, + MaybeFilledInstantSample, } from "../../../../promql/binOp"; import { formatNode, labelNameList } from "../../../../promql/format"; import { @@ -177,11 +178,10 @@ const explanationText = (node: BinaryExpr): React.ReactNode => { ) : ( - - group_{manySide}({labelNameList(matching.include)}) - - : {matching.card} match. Each series from the {oneSide}-hand side is - allowed to match with multiple series from the {manySide}-hand side. + group_{manySide} + ({labelNameList(matching.include)}) : {matching.card} match. Each + series from the {oneSide}-hand side is allowed to match with + multiple series from the {manySide}-hand side. {matching.include.length !== 0 && ( <> {" "} @@ -192,6 +192,55 @@ const explanationText = (node: BinaryExpr): React.ReactNode => { )} )} + {(matching.fillValues.lhs !== null || + matching.fillValues.rhs !== null) && + (matching.fillValues.lhs === matching.fillValues.rhs ? ( + + fill( + + {matching.fillValues.lhs} + + ) : For series on either side missing a match, fill in the sample + value{" "} + + {matching.fillValues.lhs} + + . + + ) : ( + <> + {matching.fillValues.lhs !== null && ( + + fill_left( + + {matching.fillValues.lhs} + + ) : For series on the left-hand side missing a match, fill in + the sample value{" "} + + {matching.fillValues.lhs} + + . + + )} + + {matching.fillValues.rhs !== null && ( + + fill_right + ( + + {matching.fillValues.rhs} + + ) : For series on the right-hand side missing a match, fill in + the sample value{" "} + + {matching.fillValues.rhs} + + . + + )} + + ))} {node.bool && ( bool: Instead of @@ -239,7 +288,12 @@ const explainError = ( matching: { ...(binOp.matching ? binOp.matching - : { labels: [], on: false, include: [] }), + : { + labels: [], + on: false, + include: [], + fillValues: { lhs: null, rhs: null }, + }), card: err.dupeSide === "left" ? vectorMatchCardinality.manyToOne @@ -403,7 +457,7 @@ const VectorVectorBinaryExprExplainView: FC< ); const matchGroupTable = ( - series: InstantSample[], + series: MaybeFilledInstantSample[], seriesCount: number, color: string, colorOffset?: number @@ -458,6 +512,11 @@ const VectorVectorBinaryExprExplainView: FC< )} format={true} /> + {s.filled && ( + + no match, filling in default value + + )} {showSampleValues && ( diff --git a/web/ui/mantine-ui/src/promql/ast.ts b/web/ui/mantine-ui/src/promql/ast.ts index 94872c6db0..9f8c5cb102 100644 --- a/web/ui/mantine-ui/src/promql/ast.ts +++ b/web/ui/mantine-ui/src/promql/ast.ts @@ -104,11 +104,16 @@ export interface LabelMatcher { value: string; } +export interface FillValues { + lhs: number | null; + rhs: number | null; +} export interface VectorMatching { card: vectorMatchCardinality; labels: string[]; on: boolean; include: string[]; + fillValues: FillValues; } export type StartOrEnd = "start" | "end" | null; diff --git a/web/ui/mantine-ui/src/promql/binOp.test.ts b/web/ui/mantine-ui/src/promql/binOp.test.ts index 72ef16947b..9c5d59a94c 100644 --- a/web/ui/mantine-ui/src/promql/binOp.test.ts +++ b/web/ui/mantine-ui/src/promql/binOp.test.ts @@ -81,6 +81,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -247,6 +248,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1", "label2"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -413,6 +415,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: ["same"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -579,6 +582,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricB, rhs: testMetricC, @@ -701,6 +705,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricB, rhs: testMetricC, @@ -791,6 +796,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricB, rhs: testMetricC, @@ -905,6 +911,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricC, rhs: testMetricB, @@ -1019,6 +1026,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricC, rhs: testMetricB, @@ -1107,6 +1115,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -1223,6 +1232,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -1409,6 +1419,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -1596,6 +1607,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -1763,6 +1775,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -1929,6 +1942,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -2022,6 +2036,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricB, rhs: testMetricC, @@ -2105,6 +2120,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricB, rhs: testMetricC, @@ -2156,6 +2172,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA, rhs: testMetricB, @@ -2342,6 +2359,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA.slice(0, 3), rhs: testMetricB.slice(1, 4), @@ -2474,6 +2492,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA.slice(0, 3), rhs: testMetricB.slice(1, 4), @@ -2568,6 +2587,7 @@ const testCases: TestCase[] = [ on: true, include: [], labels: ["label1"], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA.slice(0, 3), rhs: testMetricB.slice(1, 4), @@ -2700,6 +2720,7 @@ const testCases: TestCase[] = [ on: false, include: [], labels: [], + fillValues: { lhs: null, rhs: null }, }, lhs: testMetricA.slice(0, 3), rhs: testMetricB.slice(1, 4), @@ -2886,6 +2907,7 @@ describe("binOp", () => { on: true, labels: ["label1"], include: [], + fillValues: { lhs: null, rhs: null }, }; const result = resultMetric(lhs, rhs, op, matching); @@ -2911,6 +2933,7 @@ describe("binOp", () => { on: true, labels: ["label1"], include: [], + fillValues: { lhs: null, rhs: null }, }; const result = resultMetric(lhs, rhs, op, matching); @@ -2931,6 +2954,7 @@ describe("binOp", () => { on: true, labels: ["label1"], include: ["label2"], + fillValues: { lhs: null, rhs: null }, }; const result = resultMetric(lhs, rhs, op, matching); diff --git a/web/ui/mantine-ui/src/promql/binOp.ts b/web/ui/mantine-ui/src/promql/binOp.ts index dbfa64be2c..f583bf81bb 100644 --- a/web/ui/mantine-ui/src/promql/binOp.ts +++ b/web/ui/mantine-ui/src/promql/binOp.ts @@ -45,13 +45,18 @@ export type VectorMatchError = | MultipleMatchesOnBothSidesError | MultipleMatchesOnOneSideError; +export type MaybeFilledInstantSample = InstantSample & { + // If the sample was filled in via a fill(...) modifier, this is true. + filled?: boolean; +}; + // A single match group as produced by a vector-to-vector binary operation, with all of its // left-hand side and right-hand side series, as well as a result and error, if applicable. export type BinOpMatchGroup = { groupLabels: Metric; - rhs: InstantSample[]; + rhs: MaybeFilledInstantSample[]; rhsCount: number; // Number of samples before applying limits. - lhs: InstantSample[]; + lhs: MaybeFilledInstantSample[]; lhsCount: number; // Number of samples before applying limits. result: { sample: InstantSample; @@ -338,6 +343,26 @@ export const computeVectorVectorBinOp = ( groups[sig].lhsCount++; }); + // Check for any LHS / RHS with no series and fill in default values, if specified. + Object.values(groups).forEach((mg) => { + if (mg.lhs.length === 0 && matching.fillValues.lhs !== null) { + mg.lhs.push({ + metric: {}, + value: [0, formatPrometheusFloat(matching.fillValues.lhs as number)], + filled: true, + }); + mg.lhsCount = 1; + } + if (mg.rhs.length === 0 && matching.fillValues.rhs !== null) { + mg.rhs.push({ + metric: {}, + value: [0, formatPrometheusFloat(matching.fillValues.rhs as number)], + filled: true, + }); + mg.rhsCount = 1; + } + }); + // Annotate the match groups with errors (if any) and populate the results. Object.values(groups).forEach((mg) => { switch (matching.card) { diff --git a/web/ui/mantine-ui/src/promql/format.tsx b/web/ui/mantine-ui/src/promql/format.tsx index 75b1965b35..8602c65a82 100644 --- a/web/ui/mantine-ui/src/promql/format.tsx +++ b/web/ui/mantine-ui/src/promql/format.tsx @@ -265,6 +265,7 @@ const formatNodeInternal = ( case nodeType.binaryExpr: { let matching = <>; let grouping = <>; + let fill = <>; const vm = node.matching; if (vm !== null) { if ( @@ -305,6 +306,45 @@ const formatNodeInternal = ( ); } + + const lfill = vm.fillValues.lhs; + const rfill = vm.fillValues.rhs; + if (lfill !== null || rfill !== null) { + if (lfill === rfill) { + fill = ( + <> + {" "} + fill + ( + {lfill} + ) + + ); + } else { + fill = ( + <> + {lfill !== null && ( + <> + {" "} + fill_left + ( + {lfill} + ) + + )} + {rfill !== null && ( + <> + {" "} + fill_right + ( + {rfill} + ) + + )} + + ); + } + } } return ( @@ -327,7 +367,8 @@ const formatNodeInternal = ( )} {matching} - {grouping}{" "} + {grouping} + {fill}{" "} {showChildren && formatNode( maybeParenthesizeBinopChild(node.op, node.rhs), diff --git a/web/ui/mantine-ui/src/promql/serialize.ts b/web/ui/mantine-ui/src/promql/serialize.ts index 584e1ae9ff..50c32c49e4 100644 --- a/web/ui/mantine-ui/src/promql/serialize.ts +++ b/web/ui/mantine-ui/src/promql/serialize.ts @@ -135,6 +135,7 @@ const serializeNode = ( case nodeType.binaryExpr: { let matching = ""; let grouping = ""; + let fill = ""; const vm = node.matching; if (vm !== null) { if ( @@ -152,11 +153,26 @@ const serializeNode = ( ) { grouping = ` group_${vm.card === vectorMatchCardinality.manyToOne ? "left" : "right"}(${labelNameList(vm.include)})`; } + + const lfill = vm.fillValues.lhs; + const rfill = vm.fillValues.rhs; + if (lfill !== null || rfill !== null) { + if (lfill === rfill) { + fill = ` fill(${lfill})`; + } else { + if (lfill !== null) { + fill += ` fill_left(${lfill})`; + } + if (rfill !== null) { + fill += ` fill_right(${rfill})`; + } + } + } } return `${serializeNode(maybeParenthesizeBinopChild(node.op, node.lhs), childIndent, pretty)}${childSeparator}${ind}${ node.op - }${node.bool ? " bool" : ""}${matching}${grouping}${childSeparator}${serializeNode( + }${node.bool ? " bool" : ""}${matching}${grouping}${fill}${childSeparator}${serializeNode( maybeParenthesizeBinopChild(node.op, node.rhs), childIndent, pretty diff --git a/web/ui/mantine-ui/src/promql/serializeAndFormat.test.ts b/web/ui/mantine-ui/src/promql/serializeAndFormat.test.ts index a3734d311f..f9ff039882 100644 --- a/web/ui/mantine-ui/src/promql/serializeAndFormat.test.ts +++ b/web/ui/mantine-ui/src/promql/serializeAndFormat.test.ts @@ -658,6 +658,7 @@ describe("serializeNode and formatNode", () => { labels: [], on: false, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -677,6 +678,7 @@ describe("serializeNode and formatNode", () => { labels: [], on: true, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -696,6 +698,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -715,6 +718,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: false, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -735,6 +739,7 @@ describe("serializeNode and formatNode", () => { labels: [], on: false, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -755,6 +760,7 @@ describe("serializeNode and formatNode", () => { labels: [], on: false, include: ["__name__"], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -774,6 +780,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -793,6 +800,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: ["label3"], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -812,6 +820,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: [], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -831,6 +840,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: ["label3"], + fillValues: { lhs: null, rhs: null }, }, bool: false, }, @@ -864,6 +874,7 @@ describe("serializeNode and formatNode", () => { labels: ["label1", "label2"], on: true, include: ["label3"], + fillValues: { lhs: null, rhs: null }, }, bool: true, }, @@ -911,6 +922,7 @@ describe("serializeNode and formatNode", () => { include: ["c", "ü"], labels: ["b", "ö"], on: true, + fillValues: { lhs: null, rhs: null }, }, op: binaryOperatorType.div, rhs: { @@ -948,6 +960,7 @@ describe("serializeNode and formatNode", () => { include: [], labels: ["e", "ö"], on: false, + fillValues: { lhs: null, rhs: null }, }, op: binaryOperatorType.add, rhs: { diff --git a/web/ui/module/codemirror-promql/src/complete/promql.terms.ts b/web/ui/module/codemirror-promql/src/complete/promql.terms.ts index d356268d74..3670fffff7 100644 --- a/web/ui/module/codemirror-promql/src/complete/promql.terms.ts +++ b/web/ui/module/codemirror-promql/src/complete/promql.terms.ts @@ -39,6 +39,10 @@ export const binOpModifierTerms = [ { label: 'ignoring', info: 'Ignore specified labels for matching', type: 'keyword' }, { label: 'group_left', info: 'Allow many-to-one matching', type: 'keyword' }, { label: 'group_right', info: 'Allow one-to-many matching', type: 'keyword' }, + { label: 'bool', info: 'Return boolean result (0 or 1) instead of filtering', type: 'keyword' }, + { label: 'fill', info: 'Fill in missing series on both sides', type: 'keyword' }, + { label: 'fill_left', info: 'Fill in missing series on the left side', type: 'keyword' }, + { label: 'fill_right', info: 'Fill in missing series on the right side', type: 'keyword' }, ]; export const atModifierTerms = [ diff --git a/web/ui/module/codemirror-promql/src/parser/vector.test.ts b/web/ui/module/codemirror-promql/src/parser/vector.test.ts index f628206538..c6eeb930ab 100644 --- a/web/ui/module/codemirror-promql/src/parser/vector.test.ts +++ b/web/ui/module/codemirror-promql/src/parser/vector.test.ts @@ -15,29 +15,31 @@ import { buildVectorMatching } from './vector'; import { createEditorState } from '../test/utils-test'; import { BinaryExpr } from '@prometheus-io/lezer-promql'; import { syntaxTree } from '@codemirror/language'; -import { VectorMatchCardinality } from '../types'; +import { VectorMatchCardinality, VectorMatching } from '../types'; + +const noFill = { fill: { lhs: null, rhs: null } }; describe('buildVectorMatching test', () => { - const testCases = [ + const testCases: { binaryExpr: string; expectedVectorMatching: VectorMatching }[] = [ { binaryExpr: 'foo * bar', - expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [] }, + expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [], ...noFill }, }, { binaryExpr: 'foo * sum', - expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [] }, + expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [], ...noFill }, }, { binaryExpr: 'foo == 1', - expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [] }, + expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [], ...noFill }, }, { binaryExpr: 'foo == bool 1', - expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [] }, + expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [], ...noFill }, }, { binaryExpr: '2.5 / bar', - expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [] }, + expectedVectorMatching: { card: VectorMatchCardinality.CardOneToOne, matchingLabels: [], on: false, include: [], ...noFill }, }, { binaryExpr: 'foo and bar', @@ -46,6 +48,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -55,6 +58,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -64,6 +68,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -75,6 +80,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -86,6 +92,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -95,6 +102,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: true, include: [], + ...noFill, }, }, { @@ -104,6 +112,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: true, include: [], + ...noFill, }, }, { @@ -113,6 +122,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: true, include: [], + ...noFill, }, }, { @@ -122,6 +132,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: true, include: [], + ...noFill, }, }, { @@ -131,6 +142,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: false, include: [], + ...noFill, }, }, { @@ -140,6 +152,7 @@ describe('buildVectorMatching test', () => { matchingLabels: [], on: false, include: [], + ...noFill, }, }, { @@ -149,6 +162,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['bar'], on: true, include: [], + ...noFill, }, }, { @@ -158,6 +172,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: true, include: ['bar'], + ...noFill, }, }, { @@ -167,6 +182,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: false, include: ['blub'], + ...noFill, }, }, { @@ -176,6 +192,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: false, include: ['bar'], + ...noFill, }, }, { @@ -185,6 +202,7 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: true, include: ['bar', 'foo'], + ...noFill, }, }, { @@ -194,6 +212,57 @@ describe('buildVectorMatching test', () => { matchingLabels: ['test', 'blub'], on: false, include: ['bar', 'foo'], + ...noFill, + }, + }, + { + binaryExpr: 'foo + fill(23) bar', + expectedVectorMatching: { + card: VectorMatchCardinality.CardOneToOne, + matchingLabels: [], + on: false, + include: [], + fill: { lhs: 23, rhs: 23 }, + }, + }, + { + binaryExpr: 'foo + fill_left(23) bar', + expectedVectorMatching: { + card: VectorMatchCardinality.CardOneToOne, + matchingLabels: [], + on: false, + include: [], + fill: { lhs: 23, rhs: null }, + }, + }, + { + binaryExpr: 'foo + fill_right(23) bar', + expectedVectorMatching: { + card: VectorMatchCardinality.CardOneToOne, + matchingLabels: [], + on: false, + include: [], + fill: { lhs: null, rhs: 23 }, + }, + }, + { + binaryExpr: 'foo + fill_left(23) fill_right(42) bar', + expectedVectorMatching: { + card: VectorMatchCardinality.CardOneToOne, + matchingLabels: [], + on: false, + include: [], + fill: { lhs: 23, rhs: 42 }, + }, + }, + { + binaryExpr: 'foo + fill_right(23) fill_left(42) bar', + expectedVectorMatching: { + card: VectorMatchCardinality.CardOneToOne, + matchingLabels: [], + on: false, + include: [], + fill: { lhs: 42, rhs: 23 }, }, }, ]; @@ -203,7 +272,7 @@ describe('buildVectorMatching test', () => { const node = syntaxTree(state).topNode.getChild(BinaryExpr); expect(node).toBeTruthy(); if (node) { - expect(value.expectedVectorMatching).toEqual(buildVectorMatching(state, node)); + expect(buildVectorMatching(state, node)).toEqual(value.expectedVectorMatching); } }); }); diff --git a/web/ui/module/codemirror-promql/src/parser/vector.ts b/web/ui/module/codemirror-promql/src/parser/vector.ts index c47ca1fb76..9fc31bf5c6 100644 --- a/web/ui/module/codemirror-promql/src/parser/vector.ts +++ b/web/ui/module/codemirror-promql/src/parser/vector.ts @@ -24,6 +24,11 @@ import { On, Or, Unless, + NumberDurationLiteral, + FillModifier, + FillClause, + FillLeftClause, + FillRightClause, } from '@prometheus-io/lezer-promql'; import { VectorMatchCardinality, VectorMatching } from '../types'; import { containsAtLeastOneChild } from './path-finder'; @@ -37,6 +42,10 @@ export function buildVectorMatching(state: EditorState, binaryNode: SyntaxNode): matchingLabels: [], on: false, include: [], + fill: { + lhs: null, + rhs: null, + }, }; const modifierClause = binaryNode.getChild(MatchingModifierClause); if (modifierClause) { @@ -60,6 +69,32 @@ export function buildVectorMatching(state: EditorState, binaryNode: SyntaxNode): } } + const fillModifier = binaryNode.getChild(FillModifier); + if (fillModifier) { + const fill = fillModifier.getChild(FillClause); + const fillLeft = fillModifier.getChild(FillLeftClause); + const fillRight = fillModifier.getChild(FillRightClause); + + const getFillValue = (node: SyntaxNode) => { + const valueNode = node.getChild(NumberDurationLiteral); + return valueNode ? parseFloat(state.sliceDoc(valueNode.from, valueNode.to)) : null; + }; + + if (fill) { + const value = getFillValue(fill); + result.fill.lhs = value; + result.fill.rhs = value; + } + + if (fillLeft) { + result.fill.lhs = getFillValue(fillLeft); + } + + if (fillRight) { + result.fill.rhs = getFillValue(fillRight); + } + } + const isSetOperator = containsAtLeastOneChild(binaryNode, And, Or, Unless); if (isSetOperator && result.card === VectorMatchCardinality.CardOneToOne) { result.card = VectorMatchCardinality.CardManyToMany; diff --git a/web/ui/module/codemirror-promql/src/types/vector.ts b/web/ui/module/codemirror-promql/src/types/vector.ts index 4e7a4f4c45..709b0b76d6 100644 --- a/web/ui/module/codemirror-promql/src/types/vector.ts +++ b/web/ui/module/codemirror-promql/src/types/vector.ts @@ -18,6 +18,11 @@ export enum VectorMatchCardinality { CardManyToMany = 'many-to-many', } +export interface FillValues { + lhs: number | null; + rhs: number | null; +} + export interface VectorMatching { // The cardinality of the two Vectors. card: VectorMatchCardinality; @@ -30,4 +35,6 @@ export interface VectorMatching { // Include contains additional labels that should be included in // the result from the side with the lower cardinality. include: string[]; + // Fill contains optional fill values for missing elements. + fill: FillValues; } diff --git a/web/ui/module/lezer-promql/src/promql.grammar b/web/ui/module/lezer-promql/src/promql.grammar index 5fe8d4d025..9308ad01be 100644 --- a/web/ui/module/lezer-promql/src/promql.grammar +++ b/web/ui/module/lezer-promql/src/promql.grammar @@ -101,11 +101,30 @@ MatchingModifierClause { ((GroupLeft | GroupRight) (!group GroupingLabels)?)? } +FillClause { + Fill "(" NumberDurationLiteral ")" +} + +FillLeftClause { + FillLeft "(" NumberDurationLiteral ")" +} + +FillRightClause { + FillRight "(" NumberDurationLiteral ")" +} + +FillModifier { + (FillClause | FillLeftClause | FillRightClause) | + (FillLeftClause FillRightClause) | + (FillRightClause FillLeftClause) +} + BoolModifier { Bool } binModifiers { BoolModifier? MatchingModifierClause? + FillModifier? } GroupingLabels { @@ -366,7 +385,10 @@ NumberDurationLiteralInDurationContext { Start, End, Smoothed, - Anchored + Anchored, + Fill, + FillLeft, + FillRight } @external propSource promQLHighLight from "./highlight" diff --git a/web/ui/module/lezer-promql/src/tokens.js b/web/ui/module/lezer-promql/src/tokens.js index 523c306ae9..6fd681f1f8 100644 --- a/web/ui/module/lezer-promql/src/tokens.js +++ b/web/ui/module/lezer-promql/src/tokens.js @@ -12,82 +12,88 @@ // limitations under the License. import { - And, - Avg, - Atan2, - Bool, - Bottomk, - By, - Count, - CountValues, - End, - Group, - GroupLeft, - GroupRight, - Ignoring, - inf, - Max, - Min, - nan, - Offset, - On, - Or, - Quantile, - LimitK, - LimitRatio, - Start, - Stddev, - Stdvar, - Sum, - Topk, - Unless, - Without, - Smoothed, - Anchored, -} from './parser.terms.js'; + And, + Avg, + Atan2, + Bool, + Bottomk, + By, + Count, + CountValues, + End, + Group, + GroupLeft, + GroupRight, + Ignoring, + inf, + Max, + Min, + nan, + Offset, + On, + Or, + Quantile, + LimitK, + LimitRatio, + Start, + Stddev, + Stdvar, + Sum, + Topk, + Unless, + Without, + Smoothed, + Anchored, + Fill, + FillLeft, + FillRight, +} from "./parser.terms.js"; const keywordTokens = { - inf: inf, - nan: nan, - bool: Bool, - ignoring: Ignoring, - on: On, - group_left: GroupLeft, - group_right: GroupRight, - offset: Offset, + inf: inf, + nan: nan, + bool: Bool, + ignoring: Ignoring, + on: On, + group_left: GroupLeft, + group_right: GroupRight, + offset: Offset, }; export const specializeIdentifier = (value, stack) => { - return keywordTokens[value.toLowerCase()] || -1; + return keywordTokens[value.toLowerCase()] || -1; }; const contextualKeywordTokens = { - avg: Avg, - atan2: Atan2, - bottomk: Bottomk, - count: Count, - count_values: CountValues, - group: Group, - max: Max, - min: Min, - quantile: Quantile, - limitk: LimitK, - limit_ratio: LimitRatio, - stddev: Stddev, - stdvar: Stdvar, - sum: Sum, - topk: Topk, - by: By, - without: Without, - and: And, - or: Or, - unless: Unless, - start: Start, - end: End, - smoothed: Smoothed, - anchored: Anchored, + avg: Avg, + atan2: Atan2, + bottomk: Bottomk, + count: Count, + count_values: CountValues, + group: Group, + max: Max, + min: Min, + quantile: Quantile, + limitk: LimitK, + limit_ratio: LimitRatio, + stddev: Stddev, + stdvar: Stdvar, + sum: Sum, + topk: Topk, + by: By, + without: Without, + and: And, + or: Or, + unless: Unless, + start: Start, + end: End, + smoothed: Smoothed, + anchored: Anchored, + fill: Fill, + fill_left: FillLeft, + fill_right: FillRight, }; export const extendIdentifier = (value, stack) => { - return contextualKeywordTokens[value.toLowerCase()] || -1; + return contextualKeywordTokens[value.toLowerCase()] || -1; }; From 57dd1f18b4686647af93f83beead2d5fa4f2345e Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 10 Dec 2025 19:25:08 +0100 Subject: [PATCH 2/8] Add fill modifier PromQL tests Signed-off-by: Julius Volz --- promql/promqltest/testdata/fill-modifier.test | 343 ++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 promql/promqltest/testdata/fill-modifier.test diff --git a/promql/promqltest/testdata/fill-modifier.test b/promql/promqltest/testdata/fill-modifier.test new file mode 100644 index 0000000000..08c4396242 --- /dev/null +++ b/promql/promqltest/testdata/fill-modifier.test @@ -0,0 +1,343 @@ +# ==================== fill / fill_left / fill_right modifier tests ==================== + +# Test data for fill modifier tests: vectors with partial overlap. +load 5m + left_vector{label="a"} 10 + left_vector{label="b"} 20 + left_vector{label="c"} 30 + right_vector{label="a"} 100 + right_vector{label="b"} 200 + right_vector{label="d"} 400 + +# ---------- Arithmetic operators with fill modifiers ---------- + +# fill(0): Fill both sides with 0 for addition. +eval instant at 0m left_vector + fill(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 30 + {label="d"} 400 + +# fill_left(0): Only fill left side with 0. +eval instant at 0m left_vector + fill_left(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="d"} 400 + +# fill_right(0): Only fill right side with 0. +eval instant at 0m left_vector + fill_right(0) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 30 + +# fill_left and fill_right with different values. +eval instant at 0m left_vector + fill_left(5) fill_right(7) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} 37 + {label="d"} 405 + +# fill with NaN. +eval instant at 0m left_vector + fill(NaN) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} NaN + {label="d"} NaN + +# fill with Inf. +eval instant at 0m left_vector + fill(Inf) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} +Inf + {label="d"} +Inf + +# fill with -Inf. +eval instant at 0m left_vector + fill(-Inf) right_vector + {label="a"} 110 + {label="b"} 220 + {label="c"} -Inf + {label="d"} -Inf + +# ---------- Comparison operators with fill modifiers ---------- + +# fill with equality comparison. +eval instant at 0m left_vector == fill(30) right_vector + left_vector{label="c"} 30 + +# fill with inequality comparison. +eval instant at 0m left_vector != fill(30) right_vector + left_vector{label="a"} 10 + left_vector{label="b"} 20 + {label="d"} 30 + +# fill with greater than. +eval instant at 0m left_vector > fill(25) right_vector + left_vector{label="c"} 30 + +# ---------- Comparison operators with bool modifier and fill ---------- + +# fill with equality comparison and bool. +eval instant at 0m left_vector == bool fill(30) right_vector + {label="a"} 0 + {label="b"} 0 + {label="c"} 1 + {label="d"} 0 + +# fill with inequality comparison and bool. +eval instant at 0m left_vector != bool fill(30) right_vector + {label="a"} 1 + {label="b"} 1 + {label="c"} 0 + {label="d"} 1 + +# fill with greater than and bool. +eval instant at 0m left_vector > bool fill(25) right_vector + {label="a"} 0 + {label="b"} 0 + {label="c"} 1 + {label="d"} 0 + +# ---------- fill with on() and ignoring() modifiers ---------- + +clear + +load 5m + left_vector{job="foo", instance="a"} 10 + left_vector{job="foo", instance="b"} 20 + left_vector{job="bar", instance="a"} 30 + right_vector{job="foo", instance="a"} 100 + right_vector{job="foo", instance="c"} 300 + +# fill with on(). +eval instant at 0m left_vector + on(job, instance) fill(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="b"} 20 + {job="bar", instance="a"} 30 + {job="foo", instance="c"} 300 + +# fill_right with on(). +eval instant at 0m left_vector + on(job, instance) fill_right(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="b"} 20 + {job="bar", instance="a"} 30 + +# fill_left with on(). +eval instant at 0m left_vector + on(job, instance) fill_left(0) right_vector + {job="foo", instance="a"} 110 + {job="foo", instance="c"} 300 + +# fill with ignoring() - requires group_left since ignoring(job) creates many-to-one matching +# when two left_vector series have same instance but different jobs. +eval instant at 0m left_vector + ignoring(job) group_left fill(0) right_vector + {instance="a", job="foo"} 110 + {instance="a", job="bar"} 130 + {instance="b", job="foo"} 20 + {instance="c"} 300 + +# ---------- fill with group_left / group_right (many-to-one / one-to-many) ---------- + +clear + +load 5m + requests{method="GET", status="200"} 100 + requests{method="POST", status="200"} 200 + requests{method="GET", status="500"} 10 + requests{method="POST", status="500"} 20 + limits{status="200"} 1000 + limits{status="404"} 500 + limits{status="500"} 50 + +# group_left with fill_right: fill missing "one" side series. +eval instant at 0m requests / on(status) group_left fill_right(1) limits + {method="GET", status="200"} 0.1 + {method="POST", status="200"} 0.2 + {method="GET", status="500"} 0.2 + {method="POST", status="500"} 0.4 + +# group_left with fill_left: fill missing "many" side series. +# For status="404", there's no matching requests, so a single series with the match group's labels is filled +eval instant at 0m requests + on(status) group_left fill_left(0) limits + {method="GET", status="200"} 1100 + {method="POST", status="200"} 1200 + {method="GET", status="500"} 60 + {method="POST", status="500"} 70 + {status="404"} 500 + +# group_left with fill on both sides. +eval instant at 0m requests + on(status) group_left fill(0) limits + {method="GET", status="200"} 1100 + {method="POST", status="200"} 1200 + {method="GET", status="500"} 60 + {method="POST", status="500"} 70 + {status="404"} 500 + +# group_right with fill_left: fill missing "one" side series. +clear + +load 5m + cpu_info{instance="a", cpu="0"} 1 + cpu_info{instance="a", cpu="1"} 1 + cpu_info{instance="b", cpu="0"} 1 + node_meta{instance="a"} 100 + node_meta{instance="c"} 300 + +# fill_left fills the "one" side (node_meta) when missing for a "many" side series. +eval instant at 0m node_meta * on(instance) group_right fill_left(1) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="c"} 300 + +# group_right with fill_right: fill missing "many" side series. +eval instant at 0m node_meta * on(instance) group_right fill_right(0) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="b", cpu="0"} 0 + +# group_right with fill on both sides. +eval instant at 0m node_meta * on(instance) group_right fill(1) cpu_info + {instance="a", cpu="0"} 100 + {instance="a", cpu="1"} 100 + {instance="b", cpu="0"} 1 + {instance="c"} 300 + +# ---------- fill with group_left/group_right and extra labels ---------- + +clear + +load 5m + requests{method="GET", status="200"} 100 + requests{method="POST", status="200"} 200 + limits{status="200", owner="team-a"} 1000 + limits{status="500", owner="team-b"} 50 + +# group_left with extra label and fill_right. +# Note: when filling the "one" side, the joined label cannot be filled. +eval instant at 0m requests + on(status) group_left(owner) fill_right(0) limits + {method="GET", status="200", owner="team-a"} 1100 + {method="POST", status="200", owner="team-a"} 1200 + +# ---------- Edge cases ---------- + +clear + +load 5m + only_left{label="a"} 10 + only_left{label="b"} 20 + only_right{label="c"} 30 + only_right{label="d"} 40 + +# No overlap at all - fill creates all results. +eval instant at 0m only_left + fill(0) only_right + {label="a"} 10 + {label="b"} 20 + {label="c"} 30 + {label="d"} 40 + +# No overlap - fill_left only creates right side results. +eval instant at 0m only_left + fill_left(0) only_right + {label="c"} 30 + {label="d"} 40 + +# No overlap - fill_right only creates left side results. +eval instant at 0m only_left + fill_right(0) only_right + {label="a"} 10 + {label="b"} 20 + +# Complete overlap - fill has no effect. +clear + +load 5m + complete_left{label="a"} 10 + complete_left{label="b"} 20 + complete_right{label="a"} 100 + complete_right{label="b"} 200 + +eval instant at 0m complete_left + fill(99) complete_right + {label="a"} 110 + {label="b"} 220 + +# ---------- fill with range queries ---------- + +clear + +load 5m + range_left{label="a"} 1 2 3 4 5 + range_left{label="b"} 10 20 30 40 50 + range_right{label="a"} 100 200 300 400 500 + range_right{label="c"} 1000 2000 3000 4000 5000 + +eval range from 0 to 20m step 5m range_left + fill(0) range_right + {label="a"} 101 202 303 404 505 + {label="b"} 10 20 30 40 50 + {label="c"} 1000 2000 3000 4000 5000 + +eval range from 0 to 20m step 5m range_left + fill_right(0) range_right + {label="a"} 101 202 303 404 505 + {label="b"} 10 20 30 40 50 + +eval range from 0 to 20m step 5m range_left + fill_left(0) range_right + {label="a"} 101 202 303 404 505 + {label="c"} 1000 2000 3000 4000 5000 + +# Range queries with intermittently present series. +clear + +load 5m + intermittent_left{label="a"} 1 _ 3 _ 5 + intermittent_left{label="b"} _ 20 _ 40 _ + intermittent_right{label="a"} _ 200 _ 400 _ + intermittent_right{label="b"} 100 _ 300 _ 500 + intermittent_right{label="c"} 1000 _ _ 4000 5000 + +# When both sides have the same label but are present at different times, +# fill creates results at all timestamps where at least one side is present. +eval range from 0 to 20m step 5m intermittent_left + fill(0) intermittent_right + {label="a"} 1 200 3 400 5 + {label="b"} 100 20 300 40 500 + {label="c"} 1000 _ _ 4000 5000 + +# fill_right only fills the right side when it's missing. +# Output only exists when left side is present (right side filled with 0 if missing). +eval range from 0 to 20m step 5m intermittent_left + fill_right(0) intermittent_right + {label="a"} 1 _ 3 _ 5 + {label="b"} _ 20 _ 40 _ + +# fill_left only fills the left side when it's missing. +# Output only exists when right side is present (left side filled with 0 if missing). +eval range from 0 to 20m step 5m intermittent_left + fill_left(0) intermittent_right + {label="a"} _ 200 _ 400 _ + {label="b"} 100 _ 300 _ 500 + {label="c"} 1000 _ _ 4000 5000 + +# ---------- fill with vectors where one side is empty ---------- + +clear + +load 5m + non_empty{label="a"} 10 + non_empty{label="b"} 20 + +# Empty right side - fill_right has no effect (nothing to add). +eval instant at 0m non_empty + fill_right(0) nonexistent + {label="a"} 10 + {label="b"} 20 + +# Empty right side - fill_left creates nothing (no right side labels to use). +eval instant at 0m non_empty + fill_left(0) nonexistent + +# Empty left side - fill_left has no effect. +eval instant at 0m nonexistent + fill_left(0) non_empty + {label="a"} 10 + {label="b"} 20 + +# Empty left side - fill_right creates nothing. +eval instant at 0m nonexistent + fill_right(0) non_empty + +# fill both sides with one side empty. +eval instant at 0m non_empty + fill(0) nonexistent + {label="a"} 10 + {label="b"} 20 + +eval instant at 0m nonexistent + fill(0) non_empty + {label="a"} 10 + {label="b"} 20 From ce26370eeb2e19bdfac68c40a1f21913a046fddd Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Wed, 10 Dec 2025 20:07:43 +0100 Subject: [PATCH 3/8] Add PromLens binop matching explain view tests Signed-off-by: Julius Volz --- web/ui/mantine-ui/src/promql/binOp.test.ts | 431 +++++++++++++++++++++ web/ui/mantine-ui/src/promql/binOp.ts | 4 +- 2 files changed, 433 insertions(+), 2 deletions(-) diff --git a/web/ui/mantine-ui/src/promql/binOp.test.ts b/web/ui/mantine-ui/src/promql/binOp.test.ts index 9c5d59a94c..76dd24fa79 100644 --- a/web/ui/mantine-ui/src/promql/binOp.test.ts +++ b/web/ui/mantine-ui/src/promql/binOp.test.ts @@ -2163,6 +2163,437 @@ const testCases: TestCase[] = [ numGroups: 2, }, }, + { + // metric_a - fill(0) metric_b + desc: "subtraction with fill(0) but no missing series", + op: binaryOperatorType.sub, + matching: { + card: vectorMatchCardinality.oneToOne, + on: false, + include: [], + labels: [], + fillValues: { lhs: 0, rhs: 0 }, + }, + lhs: testMetricA, + rhs: testMetricB, + result: { + groups: { + [fnv1a(["a", "x", "same"])]: { + groupLabels: { label1: "a", label2: "x", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "a", + label2: "x", + same: "same", + }, + value: [0, "1"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "a", + label2: "x", + same: "same", + }, + value: [0, "10"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "a", label2: "x", same: "same" }, + value: [0, "-9"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["a", "y", "same"])]: { + groupLabels: { label1: "a", label2: "y", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "a", + label2: "y", + same: "same", + }, + value: [0, "2"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "a", + label2: "y", + same: "same", + }, + value: [0, "20"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "a", label2: "y", same: "same" }, + value: [0, "-18"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["b", "x", "same"])]: { + groupLabels: { label1: "b", label2: "x", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "b", + label2: "x", + same: "same", + }, + value: [0, "3"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "b", + label2: "x", + same: "same", + }, + value: [0, "30"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "b", label2: "x", same: "same" }, + value: [0, "-27"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["b", "y", "same"])]: { + groupLabels: { label1: "b", label2: "y", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "b", + label2: "y", + same: "same", + }, + value: [0, "4"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "b", + label2: "y", + same: "same", + }, + value: [0, "40"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "b", label2: "y", same: "same" }, + value: [0, "-36"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + }, + numGroups: 4, + }, + }, + { + // metric_a[0..2] - fill_left(23) fill_right(42) metric_b[1...3] + desc: "subtraction with different fill values and missing series on each side", + op: binaryOperatorType.sub, + matching: { + card: vectorMatchCardinality.oneToOne, + on: false, + include: [], + labels: [], + fillValues: { lhs: 23, rhs: 42 }, + }, + lhs: testMetricA.slice(0, 3), + rhs: testMetricB.slice(1, 4), + result: { + groups: { + [fnv1a(["a", "x", "same"])]: { + groupLabels: { label1: "a", label2: "x", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "a", + label2: "x", + same: "same", + }, + value: [0, "1"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + label1: "a", + label2: "x", + same: "same", + }, + value: [0, "42"], + filled: true, + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "a", label2: "x", same: "same" }, + value: [0, "-41"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["a", "y", "same"])]: { + groupLabels: { label1: "a", label2: "y", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "a", + label2: "y", + same: "same", + }, + value: [0, "2"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "a", + label2: "y", + same: "same", + }, + value: [0, "20"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "a", label2: "y", same: "same" }, + value: [0, "-18"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["b", "x", "same"])]: { + groupLabels: { label1: "b", label2: "x", same: "same" }, + lhs: [ + { + metric: { + __name__: "metric_a", + label1: "b", + label2: "x", + same: "same", + }, + value: [0, "3"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "b", + label2: "x", + same: "same", + }, + value: [0, "30"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "b", label2: "x", same: "same" }, + value: [0, "-27"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + [fnv1a(["b", "y", "same"])]: { + groupLabels: { label1: "b", label2: "y", same: "same" }, + lhs: [ + { + metric: { + label1: "b", + label2: "y", + same: "same", + }, + filled: true, + value: [0, "23"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { + __name__: "metric_b", + label1: "b", + label2: "y", + same: "same", + }, + value: [0, "40"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "b", label2: "y", same: "same" }, + value: [0, "-17"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + }, + numGroups: 4, + }, + }, + { + // metric_b[0...1] - on(label1) group_left fill(0) metric_c + desc: "many-to-one matching with matching labels specified, group_left, and fill specified", + op: binaryOperatorType.sub, + matching: { + card: vectorMatchCardinality.manyToOne, + on: true, + include: [], + labels: ["label1"], + fillValues: { lhs: 0, rhs: 0 }, + }, + lhs: testMetricB.slice(0, 2), + rhs: testMetricC, + result: { + groups: { + [fnv1a(["a"])]: { + groupLabels: { label1: "a" }, + lhs: [ + { + metric: { + __name__: "metric_b", + label1: "a", + label2: "x", + same: "same", + }, + value: [0, "10"], + }, + { + metric: { + __name__: "metric_b", + label1: "a", + label2: "y", + same: "same", + }, + value: [0, "20"], + }, + ], + lhsCount: 2, + rhs: [ + { + metric: { __name__: "metric_c", label1: "a" }, + value: [0, "100"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "a", label2: "x", same: "same" }, + value: [0, "-90"], + }, + manySideIdx: 0, + }, + { + sample: { + metric: { label1: "a", label2: "y", same: "same" }, + value: [0, "-80"], + }, + manySideIdx: 1, + }, + ], + error: null, + }, + [fnv1a(["b"])]: { + groupLabels: { label1: "b" }, + lhs: [ + { + metric: { + label1: "b", + }, + filled: true, + value: [0, "0"], + }, + ], + lhsCount: 1, + rhs: [ + { + metric: { __name__: "metric_c", label1: "b" }, + value: [0, "200"], + }, + ], + rhsCount: 1, + result: [ + { + sample: { + metric: { label1: "b" }, + value: [0, "-200"], + }, + manySideIdx: 0, + }, + ], + error: null, + }, + }, + numGroups: 2, + }, + }, { // metric_a and metric b desc: "and operator with no matching labels and matching groups", diff --git a/web/ui/mantine-ui/src/promql/binOp.ts b/web/ui/mantine-ui/src/promql/binOp.ts index f583bf81bb..9ebee90f64 100644 --- a/web/ui/mantine-ui/src/promql/binOp.ts +++ b/web/ui/mantine-ui/src/promql/binOp.ts @@ -347,7 +347,7 @@ export const computeVectorVectorBinOp = ( Object.values(groups).forEach((mg) => { if (mg.lhs.length === 0 && matching.fillValues.lhs !== null) { mg.lhs.push({ - metric: {}, + metric: mg.groupLabels, value: [0, formatPrometheusFloat(matching.fillValues.lhs as number)], filled: true, }); @@ -355,7 +355,7 @@ export const computeVectorVectorBinOp = ( } if (mg.rhs.length === 0 && matching.fillValues.rhs !== null) { mg.rhs.push({ - metric: {}, + metric: mg.groupLabels, value: [0, formatPrometheusFloat(matching.fillValues.rhs as number)], filled: true, }); From 4c9795221073defa19d662c366c0ff1f0fca0e97 Mon Sep 17 00:00:00 2001 From: Julius Volz Date: Thu, 11 Dec 2025 12:29:48 +0100 Subject: [PATCH 4/8] Document new fill binop modifiers Signed-off-by: Julius Volz --- docs/querying/operators.md | 117 ++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 35 deletions(-) diff --git a/docs/querying/operators.md b/docs/querying/operators.md index b320d8e86e..c5b01aff71 100644 --- a/docs/querying/operators.md +++ b/docs/querying/operators.md @@ -47,9 +47,9 @@ special values like `NaN`, `+Inf`, and `-Inf`. scalar that is the result of the operator applied to both scalar operands. **Between an instant vector and a scalar**, the operator is applied to the -value of every data sample in the vector. +value of every data sample in the vector. -If the data sample is a float, the operation is performed between that float and the scalar. +If the data sample is a float, the operation is performed between that float and the scalar. For example, if an instant vector of float samples is multiplied by 2, the result is another vector of float samples in which every sample value of the original vector is multiplied by 2. @@ -81,8 +81,9 @@ following: **Between two instant vectors**, a binary arithmetic operator is applied to each entry in the LHS vector and its [matching element](#vector-matching) in the RHS vector. The result is propagated into the result vector with the -grouping labels becoming the output label set. Entries for which no matching -entry in the right-hand vector can be found are not part of the result. +grouping labels becoming the output label set. By default, series for which +no matching entry in the opposite vector can be found are not part of the +result. This behavior can be adjusted using [fill modifiers](#filling-in-missing-matches). If two float samples are matched, the arithmetic operator is applied to the two input values. @@ -97,7 +98,7 @@ If two histogram samples are matched, only `+` and `-` are valid operations, each adding or subtracting all matching bucket populations and the count and the sum of observations. All other operations result in the removal of the corresponding element from the output vector, flagged by an info-level -annotation. The `+` and -` operations should generally only be applied to gauge +annotation. The `+` and `-` operations should generally only be applied to gauge histograms, but PromQL allows them for counter histograms, too, to cover specific use cases, for which special attention is required to avoid problems with unaligned counter resets. (Certain incompatibilities of counter resets can @@ -106,7 +107,7 @@ two counter histograms results in a counter histogram. All other combination of operands and all subtractions result in a gauge histogram. **In any arithmetic binary operation involving vectors**, the metric name is -dropped. This occurs even if `__name__` is explicitly mentioned in `on` +dropped. This occurs even if `__name__` is explicitly mentioned in `on` (see https://github.com/prometheus/prometheus/issues/16631 for further discussion). **For any arithmetic binary operation that may result in a negative @@ -156,9 +157,9 @@ info-level annotation. applied to matching entries. Vector elements for which the expression is not true or which do not find a match on the other side of the expression get dropped from the result, while the others are propagated into a result vector -with the grouping labels becoming the output label set. +with the grouping labels becoming the output label set. -Matches between two float samples work as usual. +Matches between two float samples work as usual. Matches between a float sample and a histogram sample are invalid, and the corresponding element is removed from the result vector, flagged by an info-level @@ -171,8 +172,8 @@ comparison binary operations are again invalid. modifier changes the behavior in the following ways: * Vector elements which find a match on the other side of the expression but for - which the expression is false instead have the value `0` and vector elements - that do find a match and for which the expression is true have the value `1`. + which the expression is false instead have the value `0`, and vector elements + that do find a match and for which the expression is true have the value `1`. (Note that elements with no match or invalid operations involving histogram samples still return no result rather than the value `0`.) * The metric name is dropped. @@ -216,11 +217,10 @@ matching behavior: One-to-one and many-to-one/one-to-many. ### Vector matching keywords -These vector matching keywords allow for matching between series with different label sets -providing: +These vector matching keywords allow for matching between series with different label sets: -* `on` -* `ignoring` +* `on(