diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go index e513926da6..d30800b2a3 100644 --- a/cmd/prometheus/main.go +++ b/cmd/prometheus/main.go @@ -249,6 +249,9 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error { case "promql-experimental-functions": parser.EnableExperimentalFunctions = true logger.Info("Experimental PromQL functions enabled.") + case "promql-duration-expr": + parser.ExperimentalDurationExpr = true + logger.Info("Experimental duration expression parsing enabled.") case "native-histograms": c.tsdb.EnableNativeHistograms = true c.scrape.EnableNativeHistogramsIngestion = true @@ -539,7 +542,7 @@ func main() { a.Flag("scrape.discovery-reload-interval", "Interval used by scrape manager to throttle target groups updates."). Hidden().Default("5s").SetValue(&cfg.scrape.DiscoveryReloadInterval) - a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, extra-scrape-metrics, auto-gomaxprocs, native-histograms, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, old-ui, otlp-deltatocumulative. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details."). + a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, extra-scrape-metrics, auto-gomaxprocs, native-histograms, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, old-ui, otlp-deltatocumulative, promql-duration-expr. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details."). Default("").StringsVar(&cfg.featureList) a.Flag("agent", "Run Prometheus in 'Agent mode'.").BoolVar(&agentMode) diff --git a/docs/command-line/prometheus.md b/docs/command-line/prometheus.md index 5124255316..ebd6007f7b 100644 --- a/docs/command-line/prometheus.md +++ b/docs/command-line/prometheus.md @@ -61,7 +61,7 @@ The Prometheus monitoring server | --query.timeout | Maximum time a query may take before being aborted. Use with server mode only. | `2m` | | --query.max-concurrency | Maximum number of queries executed concurrently. Use with server mode only. | `20` | | --query.max-samples | Maximum number of samples a single query can load into memory. Note that queries will fail if they try to load more samples than this into memory, so this also limits the number of samples a query can return. Use with server mode only. | `50000000` | -| --enable-feature ... | Comma separated feature names to enable. Valid options: exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, extra-scrape-metrics, auto-gomaxprocs, native-histograms, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, old-ui, otlp-deltatocumulative. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | | +| --enable-feature ... | Comma separated feature names to enable. Valid options: exemplar-storage, expand-external-labels, memory-snapshot-on-shutdown, promql-per-step-stats, promql-experimental-functions, extra-scrape-metrics, auto-gomaxprocs, native-histograms, created-timestamp-zero-ingestion, concurrent-rule-eval, delayed-compaction, old-ui, otlp-deltatocumulative, promql-duration-expr. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | | | --agent | Run Prometheus in 'Agent mode'. | | | --log.level | Only log messages with the given severity or above. One of: [debug, info, warn, error] | `info` | | --log.format | Output format of log messages. One of: [logfmt, json] | `logfmt` | diff --git a/docs/feature_flags.md b/docs/feature_flags.md index 6973d6d73b..402feb44f8 100644 --- a/docs/feature_flags.md +++ b/docs/feature_flags.md @@ -183,4 +183,26 @@ This state is periodically ([`max_stale`][d2c]) cleared of inactive series. Enabling this _can_ have negative impact on performance, because the in-memory state is mutex guarded. Cumulative-only OTLP requests are not affected. +### PromQL arithmetic expressions in time durations + +`--enable-feature=promql-duration-expr` + +With this flag, arithmetic expressions can also be used in time durations. The following operators are supported: + +* `+` - addition +* `-` - subtraction +* `*` - multiplication +* `/` - division +* `%` - modulo +* `^` - exponentiation + +Examples: + + 5m * 2 # Equivalent to 10m or 600s + 10m - 1m # Equivalent to 9m or 540s + (5+2) * 1m # Equivalent to 7m or 420s + 1h / 2 # Equivalent to 30m or 1800s + 4h % 3h # Equivalent to 1h or 3600s + (2 ^ 3) * 1m # Equivalent to 8m or 480s + [d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor diff --git a/promql/engine_test.go b/promql/engine_test.go index 5dddebe5df..281e4524f9 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -1900,15 +1900,6 @@ func TestSubquerySelector(t *testing.T) { }, Start: time.Unix(35, 0), }, - { - Query: "metric[0:10s]", - Result: promql.Result{ - nil, - promql.Matrix{}, - nil, - }, - Start: time.Unix(10, 0), - }, }, }, { @@ -3268,11 +3259,6 @@ func TestInstantQueryWithRangeVectorSelector(t *testing.T) { }, }, }, - "matches series but range is 0": { - expr: "some_metric[0]", - ts: baseT.Add(2 * time.Minute), - expected: promql.Matrix{}, - }, } for name, testCase := range testCases { diff --git a/promql/parser/generated_parser.y b/promql/parser/generated_parser.y index cdb4532d3b..d9f9eb5949 100644 --- a/promql/parser/generated_parser.y +++ b/promql/parser/generated_parser.y @@ -186,7 +186,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 on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector +%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 on_or_ignoring paren_expr string_literal subquery_expr unary_expr vector_selector duration_expr paren_duration_expr positive_duration_expr %start start @@ -433,14 +433,30 @@ paren_expr : LEFT_PAREN expr RIGHT_PAREN * Offset modifiers. */ -offset_expr: expr OFFSET number_duration_literal +positive_duration_expr : duration_expr { - numLit, _ := $3.(*NumberLiteral) - dur := time.Duration(numLit.Val * 1000) * time.Millisecond + numLit, ok := $1.(*NumberLiteral) + if !ok { + // This should never happen but handle it gracefully. + yylex.(*parser).addParseErrf(posrange.PositionRange{}, "internal error: duration expression did not evaluate to a number") + $$ = &NumberLiteral{Val: 1} // Use 1 as fallback to prevent cascading errors. + } else if numLit.Val > 0 { + $$ = numLit + } else { + yylex.(*parser).addParseErrf(numLit.PosRange, "duration must be greater than 0") + $$ = &NumberLiteral{Val: 1, PosRange: numLit.PosRange} // Use 1 as fallback. + } + } + ; + +offset_expr: expr OFFSET duration_expr + { + numLit, _ := $3.(*NumberLiteral) + dur := time.Duration(numLit.Val * 1000) * time.Millisecond yylex.(*parser).addOffset($1, dur) $$ = $1 } - | expr OFFSET SUB number_duration_literal + | expr OFFSET SUB duration_expr { numLit, _ := $4.(*NumberLiteral) dur := time.Duration(numLit.Val * 1000) * time.Millisecond @@ -450,6 +466,7 @@ offset_expr: expr OFFSET number_duration_literal | expr OFFSET error { yylex.(*parser).unexpected("offset", "number or duration"); $$ = $1 } ; + /* * @ modifiers. */ @@ -474,7 +491,7 @@ at_modifier_preprocessors: START | END; * Subquery and range selectors. */ -matrix_selector : expr LEFT_BRACKET number_duration_literal RIGHT_BRACKET +matrix_selector : expr LEFT_BRACKET positive_duration_expr RIGHT_BRACKET { var errMsg string vs, ok := $1.(*VectorSelector) @@ -500,7 +517,7 @@ matrix_selector : expr LEFT_BRACKET number_duration_literal RIGHT_BRACKET } ; -subquery_expr : expr LEFT_BRACKET number_duration_literal COLON number_duration_literal RIGHT_BRACKET +subquery_expr : expr LEFT_BRACKET positive_duration_expr COLON positive_duration_expr RIGHT_BRACKET { numLitRange, _ := $3.(*NumberLiteral) numLitStep, _ := $5.(*NumberLiteral) @@ -511,7 +528,7 @@ subquery_expr : expr LEFT_BRACKET number_duration_literal COLON number_duratio EndPos: $6.Pos + 1, } } - | expr LEFT_BRACKET number_duration_literal COLON RIGHT_BRACKET + | expr LEFT_BRACKET positive_duration_expr COLON RIGHT_BRACKET { numLitRange, _ := $3.(*NumberLiteral) $$ = &SubqueryExpr{ @@ -521,11 +538,11 @@ subquery_expr : expr LEFT_BRACKET number_duration_literal COLON number_duratio EndPos: $5.Pos + 1, } } - | expr LEFT_BRACKET number_duration_literal COLON number_duration_literal error + | expr LEFT_BRACKET positive_duration_expr COLON positive_duration_expr error { yylex.(*parser).unexpected("subquery selector", "\"]\""); $$ = $1 } - | expr LEFT_BRACKET number_duration_literal COLON error + | expr LEFT_BRACKET positive_duration_expr COLON error { yylex.(*parser).unexpected("subquery selector", "number or duration or \"]\""); $$ = $1 } - | expr LEFT_BRACKET number_duration_literal error + | expr LEFT_BRACKET positive_duration_expr error { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\""); $$ = $1 } | expr LEFT_BRACKET error { yylex.(*parser).unexpected("subquery selector", "number or duration"); $$ = $1 } @@ -997,4 +1014,43 @@ maybe_grouping_labels: /* empty */ { $$ = nil } | grouping_labels ; +/* + * Duration expressions. + */ + +duration_expr : number_duration_literal + /* Gives the rule the same precedence as MUL. This aligns with mathematical conventions. */ + | unary_op duration_expr %prec MUL + { + nl, ok := $2.(*NumberLiteral) + if !ok { + yylex.(*parser).addParseErrf($1.PositionRange(), "expected number literal in duration expression") + $$ = &NumberLiteral{Val: 0} + break + } + if $1.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = $1.Pos + $$ = nl + } + | duration_expr ADD duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | duration_expr SUB duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | duration_expr MUL duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | duration_expr DIV duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | duration_expr MOD duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | duration_expr POW duration_expr + { $$ = yylex.(*parser).evalDurationExprBinOp($1, $3, $2) } + | paren_duration_expr + ; + +paren_duration_expr : LEFT_PAREN duration_expr RIGHT_PAREN + { $$ = $2 } + ; + %% diff --git a/promql/parser/generated_parser.y.go b/promql/parser/generated_parser.y.go index 78d5e15245..26a7914d6d 100644 --- a/promql/parser/generated_parser.y.go +++ b/promql/parser/generated_parser.y.go @@ -251,323 +251,334 @@ var yyExca = [...]int16{ 1, -1, -2, 0, -1, 37, - 1, 141, - 10, 141, - 24, 141, + 1, 142, + 10, 142, + 24, 142, -2, 0, -1, 61, - 2, 184, - 15, 184, - 79, 184, - 85, 184, - -2, 102, - -1, 62, 2, 185, 15, 185, 79, 185, 85, 185, -2, 103, - -1, 63, + -1, 62, 2, 186, 15, 186, 79, 186, 85, 186, - -2, 105, - -1, 64, + -2, 104, + -1, 63, 2, 187, 15, 187, 79, 187, 85, 187, -2, 106, - -1, 65, + -1, 64, 2, 188, 15, 188, 79, 188, 85, 188, -2, 107, - -1, 66, + -1, 65, 2, 189, 15, 189, 79, 189, 85, 189, - -2, 112, - -1, 67, + -2, 108, + -1, 66, 2, 190, 15, 190, 79, 190, 85, 190, - -2, 114, - -1, 68, + -2, 113, + -1, 67, 2, 191, 15, 191, 79, 191, 85, 191, - -2, 116, - -1, 69, + -2, 115, + -1, 68, 2, 192, 15, 192, 79, 192, 85, 192, -2, 117, - -1, 70, + -1, 69, 2, 193, 15, 193, 79, 193, 85, 193, -2, 118, - -1, 71, + -1, 70, 2, 194, 15, 194, 79, 194, 85, 194, -2, 119, - -1, 72, + -1, 71, 2, 195, 15, 195, 79, 195, 85, 195, -2, 120, - -1, 73, + -1, 72, 2, 196, 15, 196, 79, 196, 85, 196, - -2, 124, - -1, 74, + -2, 121, + -1, 73, 2, 197, 15, 197, 79, 197, 85, 197, -2, 125, - -1, 204, - 9, 246, - 12, 246, - 13, 246, - 18, 246, - 19, 246, - 25, 246, - 41, 246, - 47, 246, - 48, 246, - 51, 246, - 57, 246, - 62, 246, - 63, 246, - 64, 246, - 65, 246, - 66, 246, - 67, 246, - 68, 246, - 69, 246, - 70, 246, - 71, 246, - 72, 246, - 73, 246, - 74, 246, - 75, 246, - 79, 246, - 83, 246, - 85, 246, - 88, 246, - 89, 246, + -1, 74, + 2, 198, + 15, 198, + 79, 198, + 85, 198, + -2, 126, + -1, 209, + 9, 247, + 12, 247, + 13, 247, + 18, 247, + 19, 247, + 25, 247, + 41, 247, + 47, 247, + 48, 247, + 51, 247, + 57, 247, + 62, 247, + 63, 247, + 64, 247, + 65, 247, + 66, 247, + 67, 247, + 68, 247, + 69, 247, + 70, 247, + 71, 247, + 72, 247, + 73, 247, + 74, 247, + 75, 247, + 79, 247, + 83, 247, + 85, 247, + 88, 247, + 89, 247, -2, 0, - -1, 205, - 9, 246, - 12, 246, - 13, 246, - 18, 246, - 19, 246, - 25, 246, - 41, 246, - 47, 246, - 48, 246, - 51, 246, - 57, 246, - 62, 246, - 63, 246, - 64, 246, - 65, 246, - 66, 246, - 67, 246, - 68, 246, - 69, 246, - 70, 246, - 71, 246, - 72, 246, - 73, 246, - 74, 246, - 75, 246, - 79, 246, - 83, 246, - 85, 246, - 88, 246, - 89, 246, + -1, 210, + 9, 247, + 12, 247, + 13, 247, + 18, 247, + 19, 247, + 25, 247, + 41, 247, + 47, 247, + 48, 247, + 51, 247, + 57, 247, + 62, 247, + 63, 247, + 64, 247, + 65, 247, + 66, 247, + 67, 247, + 68, 247, + 69, 247, + 70, 247, + 71, 247, + 72, 247, + 73, 247, + 74, 247, + 75, 247, + 79, 247, + 83, 247, + 85, 247, + 88, 247, + 89, 247, -2, 0, } const yyPrivate = 57344 -const yyLast = 803 +const yyLast = 882 var yyAct = [...]int16{ - 154, 338, 336, 157, 343, 230, 39, 196, 280, 44, - 295, 294, 84, 120, 82, 233, 180, 109, 108, 350, - 351, 352, 353, 110, 111, 243, 202, 158, 203, 135, - 112, 249, 361, 6, 333, 329, 113, 332, 232, 204, - 205, 308, 271, 60, 130, 270, 297, 268, 162, 315, - 156, 360, 153, 306, 359, 344, 200, 162, 161, 55, - 245, 246, 222, 115, 247, 116, 107, 161, 269, 54, - 267, 114, 260, 306, 182, 234, 236, 238, 239, 240, - 248, 250, 253, 254, 255, 256, 257, 261, 262, 163, - 122, 235, 237, 241, 242, 244, 251, 252, 192, 328, - 111, 258, 259, 117, 190, 164, 112, 152, 103, 55, - 106, 337, 77, 113, 184, 151, 35, 165, 327, 54, - 175, 191, 169, 172, 183, 185, 167, 189, 168, 2, - 3, 4, 5, 107, 198, 105, 159, 160, 201, 186, - 188, 7, 326, 206, 207, 208, 209, 210, 211, 212, - 213, 214, 215, 216, 217, 218, 219, 220, 199, 194, - 89, 91, 221, 162, 264, 325, 197, 223, 224, 171, - 200, 100, 101, 161, 162, 103, 104, 106, 90, 263, - 233, 324, 170, 162, 161, 323, 362, 322, 321, 274, - 243, 122, 266, 161, 131, 163, 249, 272, 123, 320, - 229, 319, 105, 232, 275, 318, 163, 317, 121, 85, - 316, 164, 163, 292, 293, 163, 265, 296, 129, 83, - 276, 86, 164, 273, 10, 245, 246, 187, 164, 247, - 88, 164, 86, 50, 79, 36, 298, 260, 1, 78, - 234, 236, 238, 239, 240, 248, 250, 253, 254, 255, - 256, 257, 261, 262, 123, 49, 235, 237, 241, 242, - 244, 251, 252, 181, 121, 182, 258, 259, 128, 48, - 127, 304, 119, 305, 307, 59, 309, 86, 9, 9, - 47, 46, 134, 310, 311, 136, 137, 138, 139, 140, - 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 45, 43, 132, 173, 179, 184, 166, 85, 330, 178, - 331, 42, 133, 55, 41, 183, 185, 83, 339, 340, - 341, 335, 177, 54, 342, 81, 346, 345, 348, 347, - 86, 303, 40, 314, 354, 355, 302, 55, 51, 356, - 53, 77, 300, 56, 195, 358, 22, 54, 313, 55, - 174, 301, 227, 57, 8, 312, 226, 357, 37, 54, - 363, 299, 126, 277, 87, 193, 228, 125, 80, 75, - 349, 225, 155, 58, 231, 18, 19, 52, 118, 20, - 124, 0, 0, 0, 0, 76, 0, 0, 0, 0, - 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, - 71, 72, 73, 74, 0, 0, 0, 13, 0, 0, - 0, 24, 0, 30, 0, 0, 31, 32, 55, 38, - 107, 53, 77, 0, 56, 279, 0, 22, 54, 0, - 0, 0, 278, 0, 57, 0, 282, 283, 281, 288, - 290, 287, 289, 284, 285, 286, 291, 0, 91, 0, - 75, 0, 0, 0, 0, 0, 18, 19, 100, 101, - 20, 0, 103, 0, 106, 90, 76, 0, 0, 0, - 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, 73, 74, 0, 0, 0, 13, 105, - 0, 0, 24, 0, 30, 0, 55, 31, 32, 53, - 77, 0, 56, 334, 0, 22, 54, 0, 0, 0, - 0, 0, 57, 0, 282, 283, 281, 288, 290, 287, - 289, 284, 285, 286, 291, 0, 0, 0, 75, 0, - 0, 0, 0, 0, 18, 19, 0, 0, 20, 0, - 0, 0, 17, 77, 76, 0, 0, 0, 22, 61, + 158, 358, 356, 161, 363, 243, 39, 201, 293, 44, + 308, 307, 84, 120, 82, 246, 185, 109, 108, 160, + 135, 110, 169, 58, 111, 256, 166, 162, 326, 171, + 112, 262, 209, 210, 113, 349, 165, 287, 245, 370, + 371, 372, 373, 207, 231, 208, 353, 352, 60, 328, + 284, 381, 288, 130, 317, 326, 335, 205, 167, 166, + 258, 259, 364, 115, 260, 116, 357, 107, 289, 165, + 380, 114, 273, 379, 168, 247, 249, 251, 252, 253, + 261, 263, 266, 267, 268, 269, 270, 274, 275, 229, + 122, 248, 250, 254, 255, 257, 264, 265, 111, 230, + 228, 271, 272, 231, 112, 163, 164, 131, 117, 103, + 285, 106, 235, 113, 77, 154, 35, 154, 6, 7, + 180, 346, 174, 177, 129, 167, 172, 55, 173, 155, + 345, 155, 166, 157, 203, 151, 105, 54, 206, 191, + 193, 168, 165, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 204, 75, + 55, 176, 154, 283, 167, 154, 157, 154, 334, 344, + 54, 236, 237, 343, 175, 76, 155, 226, 229, 155, + 168, 155, 232, 333, 187, 233, 282, 234, 230, 228, + 332, 342, 231, 227, 341, 340, 122, 279, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 316, 2, 3, 4, 5, 305, 306, + 10, 339, 309, 186, 189, 187, 88, 277, 286, 338, + 79, 337, 226, 229, 188, 190, 154, 154, 154, 154, + 154, 154, 276, 230, 228, 281, 197, 231, 227, 154, + 155, 155, 155, 155, 155, 155, 310, 311, 312, 313, + 314, 315, 318, 155, 336, 189, 36, 123, 280, 196, + 85, 195, 348, 107, 184, 188, 190, 121, 323, 183, + 83, 181, 1, 322, 324, 278, 325, 327, 192, 329, + 86, 347, 182, 86, 194, 8, 330, 331, 321, 37, + 89, 91, 92, 156, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 170, 103, 104, 106, 90, 126, + 50, 55, 49, 128, 125, 127, 78, 157, 350, 48, + 351, 54, 240, 107, 47, 46, 239, 124, 359, 360, + 361, 355, 105, 134, 362, 320, 366, 365, 368, 367, + 246, 238, 55, 75, 374, 375, 45, 43, 157, 376, + 256, 91, 54, 132, 319, 378, 262, 178, 42, 76, + 242, 100, 101, 245, 133, 103, 166, 106, 90, 41, + 383, 40, 51, 123, 75, 200, 165, 59, 382, 377, + 9, 9, 290, 121, 87, 258, 259, 198, 241, 260, + 76, 119, 105, 80, 369, 159, 86, 273, 167, 244, + 247, 249, 251, 252, 253, 261, 263, 266, 267, 268, + 269, 270, 274, 275, 168, 52, 248, 250, 254, 255, + 257, 264, 265, 118, 0, 55, 271, 272, 53, 77, + 0, 56, 85, 0, 22, 54, 0, 0, 179, 0, + 199, 57, 83, 0, 166, 0, 0, 202, 0, 0, + 81, 205, 0, 0, 165, 86, 0, 75, 153, 0, + 0, 0, 0, 18, 19, 55, 0, 20, 0, 0, + 0, 157, 0, 76, 0, 54, 167, 0, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 168, 0, 0, 13, 0, 75, 0, 24, + 0, 30, 0, 0, 31, 32, 55, 38, 107, 53, + 77, 0, 56, 152, 0, 22, 54, 0, 0, 0, + 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 89, 91, 0, 75, 0, + 0, 0, 0, 0, 18, 19, 100, 101, 20, 0, + 103, 104, 106, 90, 76, 0, 0, 0, 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 0, 0, 0, 13, 0, 0, 0, - 24, 0, 30, 0, 0, 31, 32, 18, 19, 0, - 0, 20, 0, 0, 0, 17, 35, 0, 0, 0, - 0, 22, 11, 12, 14, 15, 16, 21, 23, 25, - 26, 27, 28, 29, 33, 34, 0, 0, 0, 13, - 0, 0, 0, 24, 0, 30, 0, 0, 31, 32, - 18, 19, 0, 0, 20, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 11, 12, 14, 15, 16, - 21, 23, 25, 26, 27, 28, 29, 33, 34, 107, - 0, 0, 13, 0, 0, 0, 24, 176, 30, 0, - 0, 31, 32, 0, 0, 0, 0, 0, 107, 0, - 0, 0, 0, 0, 0, 0, 89, 91, 92, 0, - 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, - 0, 103, 104, 106, 90, 89, 91, 92, 0, 93, - 94, 95, 96, 97, 98, 99, 100, 101, 102, 0, - 103, 104, 106, 90, 107, 0, 0, 0, 105, 0, + 72, 73, 74, 0, 0, 0, 13, 105, 0, 0, + 24, 0, 30, 0, 55, 31, 32, 53, 77, 0, + 56, 292, 0, 22, 54, 0, 0, 0, 291, 0, + 57, 0, 295, 296, 294, 301, 303, 300, 302, 297, + 298, 299, 304, 0, 0, 0, 75, 0, 0, 0, + 0, 0, 18, 19, 0, 0, 20, 0, 0, 0, + 17, 77, 76, 0, 0, 0, 22, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 0, 0, 0, 13, 0, 0, 0, 24, 0, + 30, 0, 0, 31, 32, 18, 19, 0, 0, 20, + 0, 0, 0, 17, 35, 0, 0, 0, 0, 22, + 11, 12, 14, 15, 16, 21, 23, 25, 26, 27, + 28, 29, 33, 34, 0, 0, 0, 13, 0, 0, + 0, 24, 0, 30, 0, 0, 31, 32, 18, 19, + 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 11, 12, 14, 15, 16, 21, 23, + 25, 26, 27, 28, 29, 33, 34, 107, 0, 0, + 13, 0, 0, 0, 24, 0, 30, 0, 0, 31, + 32, 0, 0, 0, 0, 0, 107, 0, 0, 0, + 0, 0, 0, 0, 89, 91, 92, 0, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 0, 103, + 104, 106, 90, 89, 91, 92, 0, 93, 94, 95, + 0, 97, 98, 99, 100, 101, 102, 354, 103, 104, + 106, 90, 107, 0, 0, 0, 105, 0, 295, 296, + 294, 301, 303, 300, 302, 297, 298, 299, 304, 0, + 0, 0, 0, 0, 0, 105, 0, 0, 0, 89, + 91, 92, 0, 93, 94, 0, 0, 97, 98, 0, + 100, 101, 102, 0, 103, 104, 106, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 107, 0, 0, 0, 105, 0, 0, - 0, 89, 91, 92, 0, 93, 94, 95, 0, 97, - 98, 99, 100, 101, 102, 0, 103, 104, 106, 90, - 89, 91, 92, 0, 93, 94, 0, 0, 97, 98, - 0, 100, 101, 102, 0, 103, 104, 106, 90, 0, - 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 105, + 0, 105, } var yyPact = [...]int16{ - 31, 131, 573, 573, 409, 530, -1000, -1000, -1000, 103, + 116, 109, 671, 671, 507, 628, -1000, -1000, -1000, 103, -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, 305, -1000, 228, -1000, 654, + -1000, -1000, -1000, -1000, -1000, 440, -1000, 224, -1000, 733, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 21, 98, -1000, -1000, 487, -1000, 487, 99, + -1000, -1000, 19, 98, -1000, -1000, 585, -1000, 585, 101, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 252, -1000, -1000, - 360, -1000, -1000, 266, 214, -1000, -1000, 20, -1000, -49, - -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, - -49, -49, -49, -49, -49, 50, 48, 304, 98, -55, - -1000, 167, 167, 328, -1000, 635, 52, -1000, 302, -1000, - -1000, 261, 70, -1000, -1000, 207, -1000, 102, -1000, 96, - 154, 487, -1000, -56, -41, -1000, 487, 487, 487, 487, - 487, 487, 487, 487, 487, 487, 487, 487, 487, 487, - 487, -1000, 100, -1000, -1000, 47, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, 39, 39, 350, -1000, -1000, -1000, -1000, - 178, -1000, -1000, 157, -1000, 654, -1000, -1000, 196, -1000, - 45, -1000, -1000, -1000, -1000, -1000, 43, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, 16, 171, 163, -1000, -1000, -1000, - 408, 406, 167, 167, 167, 167, 52, 52, 119, 119, - 119, 719, 700, 119, 119, 719, 52, 52, 119, 52, - 406, -1000, 24, -1000, -1000, -1000, 340, -1000, 329, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 381, -1000, -1000, + 317, -1000, -1000, 321, 120, -1000, -1000, 29, -1000, -58, + -58, -58, -58, -58, -58, -58, -58, -58, -58, -58, + -58, -58, -58, -58, -58, 466, 17, 312, 98, -55, + -1000, 159, 159, 426, -1000, 259, 53, -1000, 272, -1000, + -1000, 221, 180, -1000, -1000, 268, -1000, 269, -1000, 244, + 445, 585, -1000, -39, -48, -1000, 585, 585, 585, 585, + 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, + 585, -1000, 151, -1000, -1000, 118, -1000, 118, -1000, 97, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 50, 50, 330, + -1000, 136, -1000, -1000, -1000, 348, -1000, -1000, 220, -1000, + 733, -1000, -1000, 265, -1000, 243, -1000, -1000, -1000, -1000, + -1000, 161, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 24, + 84, 11, -1000, -1000, -1000, 584, 319, 159, 159, 159, + 159, 53, 53, 504, 504, 504, 798, 752, 504, 504, + 798, 53, 53, 504, 53, 319, 118, 118, 118, 118, + 118, 118, 47, -12, 191, 32, -1000, -1000, -1000, 343, + -1000, 276, -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, 487, -1000, -1000, -1000, -1000, -1000, - -1000, 34, 34, 15, 34, 40, 40, 331, 32, -1000, - -1000, 204, 201, 199, 195, 193, 182, 181, 179, 175, - 159, 136, -1000, -1000, -1000, -1000, -1000, -1000, 97, -1000, - -1000, -1000, 13, -1000, 654, -1000, -1000, -1000, 34, -1000, - 11, 8, 486, -1000, -1000, -1000, 54, 174, 174, 174, - 39, 41, 41, 54, 41, 54, -73, -1000, -1000, -1000, - -1000, -1000, 34, 34, -1000, -1000, -1000, 34, -1000, -1000, - -1000, -1000, -1000, -1000, 174, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, -1000, 30, -1000, 165, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 585, -1000, -1000, + -1000, -1000, -1000, -1000, 36, 36, 23, 36, 41, 41, + 166, 39, -1000, -1000, 258, 225, 223, 215, 189, 188, + 185, 167, 163, 124, 115, -1000, -1000, -1000, -1000, -1000, + 47, 47, -12, -12, -12, -12, -1000, -1000, 270, -1000, + -1000, -1000, 13, -1000, 733, -1000, -1000, -1000, 36, -1000, + 21, 20, 790, -1000, -1000, -1000, 9, 123, 123, 123, + 50, 48, 48, 9, 48, 9, -53, -1000, -1000, -1000, + -1000, -1000, 36, 36, -1000, -1000, -1000, 36, -1000, -1000, + -1000, -1000, -1000, -1000, 123, -1000, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 49, -1000, 367, -1000, -1000, -1000, -1000, } var yyPgo = [...]int16{ - 0, 378, 13, 377, 5, 16, 374, 275, 373, 372, - 12, 370, 224, 354, 368, 14, 366, 10, 11, 365, - 364, 7, 363, 8, 4, 357, 2, 1, 3, 344, - 27, 0, 338, 332, 18, 194, 314, 312, 6, 311, - 303, 17, 302, 43, 301, 9, 300, 282, 281, 280, - 269, 255, 233, 238, 235, + 0, 433, 13, 425, 5, 16, 409, 387, 23, 405, + 12, 404, 220, 295, 403, 14, 398, 10, 11, 397, + 394, 7, 392, 8, 4, 389, 2, 1, 3, 385, + 27, 0, 382, 381, 18, 107, 379, 374, 6, 368, + 367, 17, 363, 48, 357, 9, 356, 343, 335, 334, + 329, 322, 320, 29, 303, 22, 282, 266, } var yyR1 = [...]int8{ - 0, 53, 53, 53, 53, 53, 53, 53, 38, 38, + 0, 56, 56, 56, 56, 56, 56, 56, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 33, 33, 33, 33, 34, 34, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 35, 37, 37, 47, 47, 42, 42, 42, 42, 17, 17, 17, 17, 16, 16, 16, 4, 4, - 4, 39, 41, 41, 40, 40, 40, 48, 46, 46, - 46, 32, 32, 32, 9, 9, 44, 50, 50, 50, - 50, 50, 50, 51, 52, 52, 52, 43, 43, 43, - 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, - 13, 13, 7, 7, 7, 7, 7, 7, 7, 7, + 4, 39, 41, 41, 40, 40, 40, 48, 55, 46, + 46, 46, 32, 32, 32, 9, 9, 44, 50, 50, + 50, 50, 50, 50, 51, 52, 52, 52, 43, 43, + 43, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 13, 13, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 12, 12, 12, 12, - 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, - 54, 20, 20, 20, 20, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 29, 29, 29, 21, 21, 21, - 21, 22, 22, 22, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 23, 23, 24, 24, 25, 25, 25, - 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 12, 12, 12, + 12, 14, 14, 14, 15, 15, 15, 15, 15, 15, + 15, 57, 20, 20, 20, 20, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 29, 29, 29, 21, 21, + 21, 21, 22, 22, 22, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 24, 24, 25, 25, + 25, 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, 8, 8, 5, - 5, 5, 5, 45, 45, 28, 28, 30, 30, 31, - 31, 27, 26, 26, 49, 10, 18, 18, + 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, + 5, 5, 5, 5, 45, 45, 28, 28, 30, 30, + 31, 31, 27, 26, 26, 49, 10, 18, 18, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 54, } var yyR2 = [...]int8{ @@ -577,32 +588,33 @@ var yyR2 = [...]int8{ 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, 3, 4, - 3, 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, + 1, 2, 3, 2, 3, 1, 2, 3, 1, 3, + 4, 3, 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, 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, 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, 2, 2, 1, - 1, 1, 2, 1, 1, 1, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, + 2, 3, 3, 3, 3, 3, 3, 1, 3, } var yyChk = [...]int16{ - -1000, -53, 98, 99, 100, 101, 2, 10, -13, -7, + -1000, -56, 98, 99, 100, 101, 2, 10, -13, -7, -12, 62, 63, 79, 64, 65, 66, 12, 47, 48, 51, 67, 18, 68, 83, 69, 70, 71, 72, 73, - 85, 88, 89, 74, 75, 13, -54, -13, 10, -38, + 85, 88, 89, 74, 75, 13, -57, -13, 10, -38, -33, -36, -39, -44, -45, -46, -48, -49, -50, -51, -52, -32, -3, 12, 19, 9, 15, 25, -8, -7, -43, 62, 63, 64, 65, 66, 67, 68, 69, 70, @@ -614,21 +626,23 @@ var yyChk = [...]int16{ -2, 12, -10, 2, 20, 7, 2, 4, 2, 4, 24, -35, -42, -37, -47, 78, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, - -35, -45, 57, 2, -31, -9, 2, -28, -30, 88, - 89, 19, 9, 41, 57, -45, 2, -41, -34, -17, - 15, 2, -17, -40, 22, -38, 22, 20, 7, 2, - -5, 2, 4, 54, 44, 55, -5, 20, -15, 25, - 2, 25, 2, -19, 5, -29, -21, 12, -28, -30, - 16, -38, 82, 84, 80, 81, -38, -38, -38, -38, - -38, -38, -38, -38, -38, -38, -38, -38, -38, -38, - -38, -45, 15, -28, -28, 21, 6, 2, -16, 22, - -4, -6, 25, 2, 62, 78, 63, 79, 64, 65, - 66, 80, 81, 12, 82, 47, 48, 51, 67, 18, - 68, 83, 84, 69, 70, 71, 72, 73, 88, 89, - 59, 74, 75, 22, 7, 20, -2, 25, 2, 25, - 2, 26, 26, -30, 26, 41, 57, -22, 24, 17, - -23, 30, 28, 29, 35, 36, 37, 33, 31, 34, - 32, 38, -17, -17, -18, -17, -18, 22, -45, 21, + -35, -53, 57, 2, -45, -8, -54, 15, -31, -9, + 2, -28, -30, 88, 89, 19, 9, 41, 57, -55, + 2, -53, -41, -34, -17, 15, 2, -17, -40, 22, + -38, 22, 20, 7, 2, -5, 2, 4, 54, 44, + 55, -5, 20, -15, 25, 2, 25, 2, -19, 5, + -29, -21, 12, -28, -30, 16, -38, 82, 84, 80, + 81, -38, -38, -38, -38, -38, -38, -38, -38, -38, + -38, -38, -38, -38, -38, -38, 41, 57, 53, 42, + 52, 56, -53, -53, -53, 15, -28, -28, 21, 6, + 2, -16, 22, -4, -6, 25, 2, 62, 78, 63, + 79, 64, 65, 66, 80, 81, 12, 82, 47, 48, + 51, 67, 18, 68, 83, 84, 69, 70, 71, 72, + 73, 88, 89, 59, 74, 75, 22, 7, 20, -2, + 25, 2, 25, 2, 26, 26, -30, 26, 41, 57, + -22, 24, 17, -23, 30, 28, 29, 35, 36, 37, + 33, 31, 34, 32, 38, -17, -17, -18, -17, -18, + -53, -53, -53, -53, -53, -53, 22, 22, -55, 21, 2, 22, 7, 2, -38, -27, 19, -27, 26, -27, -21, -21, 24, 17, 2, 17, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 21, 2, 22, @@ -639,43 +653,45 @@ var yyChk = [...]int16{ } var yyDef = [...]int16{ - 0, -2, 129, 129, 0, 0, 7, 6, 1, 129, - 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, - 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, - 121, 122, 123, 124, 125, 0, 2, -2, 3, 4, + 0, -2, 130, 130, 0, 0, 7, 6, 1, 130, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 0, 2, -2, 3, 4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 0, 108, 233, 234, 0, 244, 0, 85, - 86, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -2, -2, -2, -2, 227, 228, 0, 5, 100, - 0, 128, 131, 0, 135, 139, 245, 140, 144, 43, + 18, 19, 0, 109, 234, 235, 0, 245, 0, 86, + 87, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, 228, 229, 0, 5, 101, + 0, 129, 132, 0, 136, 140, 246, 141, 145, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 0, 0, 0, 0, 22, - 23, 0, 0, 0, 61, 0, 83, 84, 0, 89, - 91, 0, 95, 99, 126, 0, 132, 0, 138, 0, - 143, 0, 42, 47, 48, 44, 0, 0, 0, 0, + 23, 0, 0, 0, 61, 0, 84, 85, 0, 90, + 92, 0, 96, 100, 127, 0, 133, 0, 139, 0, + 144, 0, 42, 47, 48, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 68, 0, 70, 71, 0, 73, 239, 240, 74, - 75, 235, 236, 0, 0, 0, 82, 20, 21, 24, - 0, 54, 25, 0, 63, 65, 67, 87, 0, 92, - 0, 98, 229, 230, 231, 232, 0, 127, 130, 133, - 136, 134, 137, 142, 145, 147, 150, 154, 155, 156, - 0, 26, 0, 0, -2, -2, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 69, 0, 237, 238, 76, 0, 81, 0, 53, - 56, 58, 59, 60, 198, 199, 200, 201, 202, 203, - 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, - 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, - 224, 225, 226, 62, 66, 88, 90, 93, 97, 94, - 96, 0, 0, 0, 0, 0, 0, 0, 0, 160, - 162, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 45, 46, 49, 247, 50, 72, 0, 78, - 80, 51, 0, 57, 64, 146, 241, 148, 0, 151, - 0, 0, 0, 158, 163, 159, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 77, 79, 52, - 55, 149, 0, 0, 157, 161, 164, 0, 243, 165, - 166, 167, 168, 169, 0, 170, 171, 172, 173, 174, - 180, 181, 182, 183, 152, 153, 242, 0, 178, 0, - 176, 179, 175, 177, + 0, 69, 229, 71, 249, 0, 257, 0, 72, 0, + 74, 240, 241, 75, 76, 236, 237, 0, 0, 0, + 83, 68, 20, 21, 24, 0, 54, 25, 0, 63, + 65, 67, 88, 0, 93, 0, 99, 230, 231, 232, + 233, 0, 128, 131, 134, 137, 135, 138, 143, 146, + 148, 151, 155, 156, 157, 0, 26, 0, 0, -2, + -2, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 0, 0, 0, 0, + 0, 0, 70, 250, 0, 0, 238, 239, 77, 0, + 82, 0, 53, 56, 58, 59, 60, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 62, 66, 89, 91, + 94, 98, 95, 97, 0, 0, 0, 0, 0, 0, + 0, 0, 161, 163, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 45, 46, 49, 248, 50, + 251, 252, 253, 254, 255, 256, 258, 73, 0, 79, + 81, 51, 0, 57, 64, 147, 242, 149, 0, 152, + 0, 0, 0, 159, 164, 160, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 78, 80, 52, + 55, 150, 0, 0, 158, 162, 165, 0, 244, 166, + 167, 168, 169, 170, 0, 171, 172, 173, 174, 175, + 181, 182, 183, 184, 153, 154, 243, 0, 179, 0, + 177, 180, 176, 178, } var yyTok1 = [...]int8{ @@ -1331,6 +1347,21 @@ yydefault: yyVAL.node = &ParenExpr{Expr: yyDollar[2].node.(Expr), PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item)} } case 68: + yyDollar = yyS[yypt-1 : yypt+1] + { + numLit, ok := yyDollar[1].node.(*NumberLiteral) + if !ok { + // This should never happen but handle it gracefully. + yylex.(*parser).addParseErrf(posrange.PositionRange{}, "internal error: duration expression did not evaluate to a number") + yyVAL.node = &NumberLiteral{Val: 1} // Use 1 as fallback to prevent cascading errors. + } else if numLit.Val > 0 { + yyVAL.node = numLit + } else { + yylex.(*parser).addParseErrf(numLit.PosRange, "duration must be greater than 0") + yyVAL.node = &NumberLiteral{Val: 1, PosRange: numLit.PosRange} // Use 1 as fallback. + } + } + case 69: yyDollar = yyS[yypt-3 : yypt+1] { numLit, _ := yyDollar[3].node.(*NumberLiteral) @@ -1338,7 +1369,7 @@ yydefault: yylex.(*parser).addOffset(yyDollar[1].node, dur) yyVAL.node = yyDollar[1].node } - case 69: + case 70: yyDollar = yyS[yypt-4 : yypt+1] { numLit, _ := yyDollar[4].node.(*NumberLiteral) @@ -1346,31 +1377,31 @@ yydefault: yylex.(*parser).addOffset(yyDollar[1].node, -dur) yyVAL.node = yyDollar[1].node } - case 70: + case 71: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("offset", "number or duration") yyVAL.node = yyDollar[1].node } - case 71: + case 72: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).setTimestamp(yyDollar[1].node, yyDollar[3].float) yyVAL.node = yyDollar[1].node } - case 72: + case 73: yyDollar = yyS[yypt-5 : yypt+1] { yylex.(*parser).setAtModifierPreprocessor(yyDollar[1].node, yyDollar[3].item) yyVAL.node = yyDollar[1].node } - case 73: + case 74: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("@", "timestamp") yyVAL.node = yyDollar[1].node } - case 76: + case 77: yyDollar = yyS[yypt-4 : yypt+1] { var errMsg string @@ -1395,7 +1426,7 @@ yydefault: EndPos: yylex.(*parser).lastClosing, } } - case 77: + case 78: yyDollar = yyS[yypt-6 : yypt+1] { numLitRange, _ := yyDollar[3].node.(*NumberLiteral) @@ -1407,7 +1438,7 @@ yydefault: EndPos: yyDollar[6].item.Pos + 1, } } - case 78: + case 79: yyDollar = yyS[yypt-5 : yypt+1] { numLitRange, _ := yyDollar[3].node.(*NumberLiteral) @@ -1418,31 +1449,31 @@ yydefault: EndPos: yyDollar[5].item.Pos + 1, } } - case 79: + case 80: yyDollar = yyS[yypt-6 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "\"]\"") yyVAL.node = yyDollar[1].node } - case 80: + case 81: yyDollar = yyS[yypt-5 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "number or duration or \"]\"") yyVAL.node = yyDollar[1].node } - case 81: + case 82: yyDollar = yyS[yypt-4 : yypt+1] { yylex.(*parser).unexpected("subquery or range", "\":\" or \"]\"") yyVAL.node = yyDollar[1].node } - case 82: + case 83: yyDollar = yyS[yypt-3 : yypt+1] { yylex.(*parser).unexpected("subquery selector", "number or duration") yyVAL.node = yyDollar[1].node } - case 83: + case 84: yyDollar = yyS[yypt-2 : yypt+1] { if nl, ok := yyDollar[2].node.(*NumberLiteral); ok { @@ -1455,7 +1486,7 @@ yydefault: yyVAL.node = &UnaryExpr{Op: yyDollar[1].item.Typ, Expr: yyDollar[2].node.(Expr), StartPos: yyDollar[1].item.Pos} } } - case 84: + case 85: yyDollar = yyS[yypt-2 : yypt+1] { vs := yyDollar[2].node.(*VectorSelector) @@ -1464,7 +1495,7 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 85: + case 86: yyDollar = yyS[yypt-1 : yypt+1] { vs := &VectorSelector{ @@ -1475,14 +1506,14 @@ yydefault: yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 86: + case 87: yyDollar = yyS[yypt-1 : yypt+1] { vs := yyDollar[1].node.(*VectorSelector) yylex.(*parser).assembleVectorSelector(vs) yyVAL.node = vs } - case 87: + case 88: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1490,7 +1521,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[3].item), } } - case 88: + case 89: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1498,7 +1529,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[4].item), } } - case 89: + case 90: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.node = &VectorSelector{ @@ -1506,7 +1537,7 @@ yydefault: PosRange: mergeRanges(&yyDollar[1].item, &yyDollar[2].item), } } - case 90: + case 91: yyDollar = yyS[yypt-3 : yypt+1] { if yyDollar[1].matchers != nil { @@ -1515,38 +1546,32 @@ yydefault: yyVAL.matchers = yyDollar[1].matchers } } - case 91: + case 92: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matchers = []*labels.Matcher{yyDollar[1].matcher} } - case 92: + case 93: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "\",\" or \"}\"") yyVAL.matchers = yyDollar[1].matchers } - case 93: - yyDollar = yyS[yypt-3 : yypt+1] - { - yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) - } case 94: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) } case 95: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.matcher = yylex.(*parser).newLabelMatcher(yyDollar[1].item, yyDollar[2].item, yyDollar[3].item) + } + case 96: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.matcher = yylex.(*parser).newMetricNameMatcher(yyDollar[1].item) } - case 96: - yyDollar = yyS[yypt-3 : yypt+1] - { - yylex.(*parser).unexpected("label matching", "string") - yyVAL.matcher = nil - } case 97: yyDollar = yyS[yypt-3 : yypt+1] { @@ -1554,86 +1579,86 @@ yydefault: yyVAL.matcher = nil } case 98: + yyDollar = yyS[yypt-3 : yypt+1] + { + yylex.(*parser).unexpected("label matching", "string") + yyVAL.matcher = nil + } + case 99: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label matching", "label matching operator") yyVAL.matcher = nil } - case 99: + case 100: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label matching", "identifier or \"}\"") yyVAL.matcher = nil } - case 100: + case 101: 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 101: + case 102: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.labels = yyDollar[1].labels } - case 126: - yyDollar = yyS[yypt-3 : yypt+1] - { - yyVAL.labels = labels.New(yyDollar[2].lblList...) - } case 127: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.labels = labels.New(yyDollar[2].lblList...) } case 128: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] { - yyVAL.labels = labels.New() + yyVAL.labels = labels.New(yyDollar[2].lblList...) } case 129: - yyDollar = yyS[yypt-0 : yypt+1] + yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.labels = labels.New() } case 130: + yyDollar = yyS[yypt-0 : yypt+1] + { + yyVAL.labels = labels.New() + } + case 131: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.lblList = append(yyDollar[1].lblList, yyDollar[3].label) } - case 131: + case 132: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.lblList = []labels.Label{yyDollar[1].label} } - case 132: + case 133: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\",\" or \"}\"") yyVAL.lblList = yyDollar[1].lblList } - case 133: - yyDollar = yyS[yypt-3 : yypt+1] - { - yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} - } case 134: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} } case 135: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.label = labels.Label{Name: yyDollar[1].item.Val, Value: yylex.(*parser).unquoteString(yyDollar[3].item.Val)} + } + case 136: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.label = labels.Label{Name: labels.MetricName, Value: yyDollar[1].item.Val} } - case 136: - yyDollar = yyS[yypt-3 : yypt+1] - { - yylex.(*parser).unexpected("label set", "string") - yyVAL.label = labels.Label{} - } case 137: yyDollar = yyS[yypt-3 : yypt+1] { @@ -1641,18 +1666,24 @@ yydefault: yyVAL.label = labels.Label{} } case 138: + yyDollar = yyS[yypt-3 : yypt+1] + { + yylex.(*parser).unexpected("label set", "string") + yyVAL.label = labels.Label{} + } + case 139: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("label set", "\"=\"") yyVAL.label = labels.Label{} } - case 139: + case 140: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("label set", "identifier or \"}\"") yyVAL.label = labels.Label{} } - case 140: + case 141: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).generatedParserResult = &seriesDescription{ @@ -1660,33 +1691,33 @@ yydefault: values: yyDollar[2].series, } } - case 141: + case 142: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.series = []SequenceValue{} } - case 142: + case 143: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = append(yyDollar[1].series, yyDollar[3].series...) } - case 143: + case 144: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.series = yyDollar[1].series } - case 144: + case 145: yyDollar = yyS[yypt-1 : yypt+1] { yylex.(*parser).unexpected("series values", "") yyVAL.series = nil } - case 145: + case 146: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Omitted: true}} } - case 146: + case 147: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1694,12 +1725,12 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Omitted: true}) } } - case 147: + case 148: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Value: yyDollar[1].float}} } - case 148: + case 149: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1708,7 +1739,7 @@ yydefault: yyVAL.series = append(yyVAL.series, SequenceValue{Value: yyDollar[1].float}) } } - case 149: + case 150: yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1718,12 +1749,12 @@ yydefault: yyDollar[1].float += yyDollar[2].float } } - case 150: + case 151: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.series = []SequenceValue{{Histogram: yyDollar[1].histogram}} } - case 151: + case 152: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.series = []SequenceValue{} @@ -1733,7 +1764,7 @@ yydefault: //$1 += $2 } } - case 152: + case 153: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsIncreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1742,7 +1773,7 @@ yydefault: } yyVAL.series = val } - case 153: + case 154: yyDollar = yyS[yypt-5 : yypt+1] { val, err := yylex.(*parser).histogramsDecreaseSeries(yyDollar[1].histogram, yyDollar[3].histogram, yyDollar[5].uint) @@ -1751,7 +1782,7 @@ yydefault: } yyVAL.series = val } - case 154: + case 155: yyDollar = yyS[yypt-1 : yypt+1] { if yyDollar[1].item.Val != "stale" { @@ -1759,130 +1790,130 @@ yydefault: } yyVAL.float = math.Float64frombits(value.StaleNaN) } - case 157: - yyDollar = yyS[yypt-4 : yypt+1] - { - yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) - } case 158: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } case 159: yyDollar = yyS[yypt-3 : yypt+1] { - m := yylex.(*parser).newMap() - yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) + yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&yyDollar[2].descriptors) } case 160: - yyDollar = yyS[yypt-2 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] { m := yylex.(*parser).newMap() yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) } case 161: + yyDollar = yyS[yypt-2 : yypt+1] + { + m := yylex.(*parser).newMap() + yyVAL.histogram = yylex.(*parser).buildHistogramFromMap(&m) + } + case 162: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = *(yylex.(*parser).mergeMaps(&yyDollar[1].descriptors, &yyDollar[3].descriptors)) } - case 162: + case 163: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.descriptors = yyDollar[1].descriptors } - case 163: + case 164: yyDollar = yyS[yypt-2 : yypt+1] { yylex.(*parser).unexpected("histogram description", "histogram description key, e.g. buckets:[5 10 7]") } - case 164: - yyDollar = yyS[yypt-3 : yypt+1] - { - yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["schema"] = yyDollar[3].int - } case 165: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["sum"] = yyDollar[3].float + yyVAL.descriptors["schema"] = yyDollar[3].int } case 166: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["count"] = yyDollar[3].float + yyVAL.descriptors["sum"] = yyDollar[3].float } case 167: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["z_bucket"] = yyDollar[3].float + yyVAL.descriptors["count"] = yyDollar[3].float } case 168: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["z_bucket_w"] = yyDollar[3].float + yyVAL.descriptors["z_bucket"] = yyDollar[3].float } case 169: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["custom_values"] = yyDollar[3].bucket_set + yyVAL.descriptors["z_bucket_w"] = yyDollar[3].float } case 170: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["buckets"] = yyDollar[3].bucket_set + yyVAL.descriptors["custom_values"] = yyDollar[3].bucket_set } case 171: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["offset"] = yyDollar[3].int + yyVAL.descriptors["buckets"] = yyDollar[3].bucket_set } case 172: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["n_buckets"] = yyDollar[3].bucket_set + yyVAL.descriptors["offset"] = yyDollar[3].int } case 173: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["n_offset"] = yyDollar[3].int + yyVAL.descriptors["n_buckets"] = yyDollar[3].bucket_set } case 174: yyDollar = yyS[yypt-3 : yypt+1] { yyVAL.descriptors = yylex.(*parser).newMap() - yyVAL.descriptors["counter_reset_hint"] = yyDollar[3].item + yyVAL.descriptors["n_offset"] = yyDollar[3].int } case 175: - yyDollar = yyS[yypt-4 : yypt+1] + yyDollar = yyS[yypt-3 : yypt+1] { - yyVAL.bucket_set = yyDollar[2].bucket_set + yyVAL.descriptors = yylex.(*parser).newMap() + yyVAL.descriptors["counter_reset_hint"] = yyDollar[3].item } case 176: - yyDollar = yyS[yypt-3 : yypt+1] + yyDollar = yyS[yypt-4 : yypt+1] { yyVAL.bucket_set = yyDollar[2].bucket_set } case 177: yyDollar = yyS[yypt-3 : yypt+1] { - yyVAL.bucket_set = append(yyDollar[1].bucket_set, yyDollar[3].float) + yyVAL.bucket_set = yyDollar[2].bucket_set } case 178: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.bucket_set = append(yyDollar[1].bucket_set, yyDollar[3].float) + } + case 179: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.bucket_set = []float64{yyDollar[1].float} } - case 233: + case 234: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &NumberLiteral{ @@ -1890,7 +1921,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 234: + case 235: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -1904,12 +1935,12 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 235: + case 236: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.float = yylex.(*parser).number(yyDollar[1].item.Val) } - case 236: + case 237: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -1920,17 +1951,17 @@ yydefault: } yyVAL.float = dur.Seconds() } - case 237: + case 238: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = yyDollar[2].float } - case 238: + case 239: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.float = -yyDollar[2].float } - case 241: + case 242: yyDollar = yyS[yypt-1 : yypt+1] { var err error @@ -1939,17 +1970,17 @@ yydefault: yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "invalid repetition in series values: %s", err) } } - case 242: + case 243: yyDollar = yyS[yypt-2 : yypt+1] { yyVAL.int = -int64(yyDollar[2].uint) } - case 243: + case 244: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.int = int64(yyDollar[1].uint) } - case 244: + case 245: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.node = &StringLiteral{ @@ -1957,7 +1988,7 @@ yydefault: PosRange: yyDollar[1].item.PositionRange(), } } - case 245: + case 246: yyDollar = yyS[yypt-1 : yypt+1] { yyVAL.item = Item{ @@ -1966,11 +1997,61 @@ yydefault: Val: yylex.(*parser).unquoteString(yyDollar[1].item.Val), } } - case 246: + case 247: yyDollar = yyS[yypt-0 : yypt+1] { yyVAL.strings = nil } + case 250: + yyDollar = yyS[yypt-2 : yypt+1] + { + nl, ok := yyDollar[2].node.(*NumberLiteral) + if !ok { + yylex.(*parser).addParseErrf(yyDollar[1].item.PositionRange(), "expected number literal in duration expression") + yyVAL.node = &NumberLiteral{Val: 0} + break + } + if yyDollar[1].item.Typ == SUB { + nl.Val *= -1 + } + nl.PosRange.Start = yyDollar[1].item.Pos + yyVAL.node = nl + } + case 251: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 252: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 253: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 254: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 255: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 256: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yylex.(*parser).evalDurationExprBinOp(yyDollar[1].node, yyDollar[3].node, yyDollar[2].item) + } + case 258: + yyDollar = yyS[yypt-3 : yypt+1] + { + yyVAL.node = yyDollar[2].node + } } goto yystack /* stack new state and value */ } diff --git a/promql/parser/lex.go b/promql/parser/lex.go index 52658f318c..8d78391229 100644 --- a/promql/parser/lex.go +++ b/promql/parser/lex.go @@ -277,6 +277,7 @@ type Lexer struct { braceOpen bool // Whether a { is opened. bracketOpen bool // Whether a [ is opened. gotColon bool // Whether we got a ':' after [ was opened. + gotDuration bool // Whether we got a duration after [ was opened. stringOpen rune // Quote rune of the string currently being read. // series description variables for internal PromQL testing framework as well as in promtool rules unit tests. @@ -491,7 +492,7 @@ func lexStatements(l *Lexer) stateFn { skipSpaces(l) } l.bracketOpen = true - return lexNumberOrDuration + return lexDurationExpr case r == ']': if !l.bracketOpen { return l.errorf("unexpected right bracket %q", r) @@ -549,6 +550,8 @@ func lexHistogram(l *Lexer) stateFn { return lexNumber case r == '[': l.bracketOpen = true + l.gotColon = false + l.gotDuration = false l.emit(LEFT_BRACKET) return lexBuckets case r == '}' && l.peek() == '}': @@ -1077,3 +1080,64 @@ func isDigit(r rune) bool { func isAlpha(r rune) bool { return r == '_' || ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') } + +// lexDurationExpr scans arithmetic expressions within brackets for duration expressions. +func lexDurationExpr(l *Lexer) stateFn { + switch r := l.next(); { + case r == eof: + return l.errorf("unexpected end of input in duration expression") + case r == ']': + l.emit(RIGHT_BRACKET) + l.bracketOpen = false + l.gotColon = false + return lexStatements + case r == ':': + l.emit(COLON) + if !l.gotDuration { + return l.errorf("unexpected colon before duration in duration expression") + } + if l.gotColon { + return l.errorf("unexpected repeated colon in duration expression") + } + l.gotColon = true + return lexDurationExpr + case r == '(': + l.emit(LEFT_PAREN) + l.parenDepth++ + return lexDurationExpr + case r == ')': + l.emit(RIGHT_PAREN) + l.parenDepth-- + if l.parenDepth < 0 { + return l.errorf("unexpected right parenthesis %q", r) + } + return lexDurationExpr + case isSpace(r): + skipSpaces(l) + return lexDurationExpr + case r == '+': + l.emit(ADD) + return lexDurationExpr + case r == '-': + l.emit(SUB) + return lexDurationExpr + case r == '*': + l.emit(MUL) + return lexDurationExpr + case r == '/': + l.emit(DIV) + return lexDurationExpr + case r == '%': + l.emit(MOD) + return lexDurationExpr + case r == '^': + l.emit(POW) + return lexDurationExpr + case isDigit(r) || (r == '.' && isDigit(l.peek())): + l.backup() + l.gotDuration = true + return lexNumberOrDuration + default: + return l.errorf("unexpected character in duration expression: %q", r) + } +} diff --git a/promql/parser/lex_test.go b/promql/parser/lex_test.go index c5475a8b94..3d1a128946 100644 --- a/promql/parser/lex_test.go +++ b/promql/parser/lex_test.go @@ -915,6 +915,10 @@ var tests = []struct { input: `test:name{on!~"bar"}[:4s]`, fail: true, }, + { + input: `test:name{on!~"bar"}[1s:1s:1s]`, + fail: true, + }, }, }, } diff --git a/promql/parser/parse.go b/promql/parser/parse.go index 5ace332d71..ea27a04d9e 100644 --- a/promql/parser/parse.go +++ b/promql/parser/parse.go @@ -39,6 +39,9 @@ var parserPool = sync.Pool{ }, } +// ExperimentalDurationExpr is a flag to enable experimental duration expression parsing. +var ExperimentalDurationExpr bool + type Parser interface { ParseExpr() (Expr, error) Close() @@ -881,9 +884,6 @@ func parseDuration(ds string) (time.Duration, error) { if err != nil { return 0, err } - if dur == 0 { - return 0, errors.New("duration must be greater than 0") - } return time.Duration(dur), nil } @@ -1060,3 +1060,66 @@ func MustGetFunction(name string) *Function { } return f } + +// evalDurationExprBinOp evaluates binary operations for duration expressions. +// It handles type checking, performs the operation using the specified operator, +// and constructs a new NumberLiteral with the result. +func (p *parser) evalDurationExprBinOp(lhs, rhs Node, op Item) *NumberLiteral { + if !ExperimentalDurationExpr { + p.addParseErrf(op.PositionRange(), "experimental duration expression parsing is experimental and must be enabled with --enable-feature=promql-duration-expr") + return &NumberLiteral{Val: 0} + } + + numLit1, ok1 := lhs.(*NumberLiteral) + numLit2, ok2 := rhs.(*NumberLiteral) + + if !ok1 || !ok2 { + p.addParseErrf(posrange.PositionRange{ + Start: lhs.PositionRange().Start, + End: rhs.PositionRange().End, + }, "invalid operands for %s", op.Val) + return &NumberLiteral{Val: 0} + } + + var val float64 + var err error + + switch op.Typ { + case ADD: + val = numLit1.Val + numLit2.Val + case SUB: + val = numLit1.Val - numLit2.Val + case MUL: + val = numLit1.Val * numLit2.Val + case DIV: + if numLit2.Val == 0 { + err = errors.New("division by zero") + } else { + val = numLit1.Val / numLit2.Val + } + case MOD: + if numLit2.Val == 0 { + err = errors.New("modulo by zero") + } else { + val = math.Mod(numLit1.Val, numLit2.Val) + } + case POW: + val = math.Pow(numLit1.Val, numLit2.Val) + default: + p.addParseErrf(op.PositionRange(), "unknown operator for duration expression: %s", op.Val) + return &NumberLiteral{Val: 0} + } + + if err != nil { + p.addParseErrf(numLit2.PosRange, err.Error()) + return &NumberLiteral{Val: 0} + } + + return &NumberLiteral{ + Val: val, + PosRange: posrange.PositionRange{ + Start: numLit1.PosRange.Start, + End: numLit2.PosRange.End, + }, + } +} diff --git a/promql/parser/parse_test.go b/promql/parser/parse_test.go index 64ce97304c..09612c1130 100644 --- a/promql/parser/parse_test.go +++ b/promql/parser/parse_test.go @@ -2337,12 +2337,12 @@ var testExpr = []struct { { input: `foo[]`, fail: true, - errMsg: "bad number or duration syntax: \"\"", + errMsg: "unexpected \"]\" in subquery selector, expected number or duration", }, { input: `foo[-1]`, fail: true, - errMsg: "bad number or duration syntax: \"\"", + errMsg: "duration must be greater than 0", }, { input: `some_metric[5m] OFFSET 1mm`, @@ -3091,7 +3091,7 @@ var testExpr = []struct { { input: `foo{bar="baz"}[`, fail: true, - errMsg: `1:16: parse error: bad number or duration syntax: ""`, + errMsg: `unexpected end of input in duration expression`, }, { input: `foo{bar="baz"}[10m:6s]`, @@ -3946,6 +3946,120 @@ var testExpr = []struct { }, }, }, + { + input: `foo[11s+10s-5*2^2]`, + expected: &MatrixSelector{ + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), + }, + PosRange: posrange.PositionRange{ + Start: 0, + End: 3, + }, + }, + Range: 1 * time.Second, // 11s+10s-5*2^2 = 21s-20s = 1s + EndPos: 18, + }, + }, + { + input: `foo[-(10s-5s)+20s]`, + expected: &MatrixSelector{ + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), + }, + PosRange: posrange.PositionRange{ + Start: 0, + End: 3, + }, + }, + Range: 15 * time.Second, // -(10s-5s)+20s = -5s+20s = 15s + EndPos: 18, + }, + }, + { + input: `foo[-10s+15s]`, + expected: &MatrixSelector{ + VectorSelector: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), + }, + PosRange: posrange.PositionRange{ + Start: 0, + End: 3, + }, + }, + Range: 5 * time.Second, // -10s+15s = 5s + EndPos: 13, + }, + }, + { + input: `foo[4s+4s:1s*2] offset (5s-8)`, + expected: &SubqueryExpr{ + Expr: &VectorSelector{ + Name: "foo", + LabelMatchers: []*labels.Matcher{ + MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), + }, + PosRange: posrange.PositionRange{ + Start: 0, + End: 3, + }, + }, + Range: 8 * time.Second, // 4s+4s = 8s + Step: 2 * time.Second, // 1s*2 = 2s + OriginalOffset: -3 * time.Second, // 5s-8 = -3s + EndPos: 29, + }, + }, + { + input: `foo offset 5s-8`, + expected: &BinaryExpr{ + Op: SUB, + LHS: &VectorSelector{ + Name: "foo", + OriginalOffset: 5 * time.Second, + LabelMatchers: []*labels.Matcher{ + MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), + }, + PosRange: posrange.PositionRange{ + Start: 0, + End: 13, + }, + }, + RHS: &NumberLiteral{ + Val: 8, + PosRange: posrange.PositionRange{ + Start: 14, + End: 15, + }, + }, + }, + }, + { + input: `foo[5s/0d]`, + fail: true, + errMsg: `division by zero`, + }, + { + input: `foo offset (4d/0)`, + fail: true, + errMsg: `division by zero`, + }, + { + input: `foo[5s%0d]`, + fail: true, + errMsg: `modulo by zero`, + }, + { + input: `foo offset (5s%(2d-2d))`, + fail: true, + errMsg: `modulo by zero`, + }, } func makeInt64Pointer(val int64) *int64 { @@ -3965,8 +4079,11 @@ func readable(s string) string { func TestParseExpressions(t *testing.T) { // Enable experimental functions testing. EnableExperimentalFunctions = true + // Enable experimental duration expression parsing. + ExperimentalDurationExpr = true t.Cleanup(func() { EnableExperimentalFunctions = false + ExperimentalDurationExpr = false }) for _, test := range testExpr { diff --git a/promql/promqltest/test.go b/promql/promqltest/test.go index 84ca16e8ab..ed214448da 100644 --- a/promql/promqltest/test.go +++ b/promql/promqltest/test.go @@ -117,8 +117,12 @@ func RunBuiltinTests(t TBRun, engine promql.QueryEngine) { // RunBuiltinTestsWithStorage runs an acceptance test suite against the provided engine and storage. func RunBuiltinTestsWithStorage(t TBRun, engine promql.QueryEngine, newStorage func(testutil.T) storage.Storage) { - t.Cleanup(func() { parser.EnableExperimentalFunctions = false }) + t.Cleanup(func() { + parser.EnableExperimentalFunctions = false + parser.ExperimentalDurationExpr = false + }) parser.EnableExperimentalFunctions = true + parser.ExperimentalDurationExpr = true files, err := fs.Glob(testsFs, "*/*.test") require.NoError(t, err) diff --git a/promql/promqltest/testdata/duration_expression.test b/promql/promqltest/testdata/duration_expression.test new file mode 100644 index 0000000000..251856241a --- /dev/null +++ b/promql/promqltest/testdata/duration_expression.test @@ -0,0 +1,121 @@ +# Test for different duration expression formats in range selectors. +# This tests the parser's ability to handle various duration expression. + +# Set up a basic counter that increases steadily. +load 5m + http_requests{path="/foo"} 1 2 3 0 1 0 0 1 2 0 + http_requests{path="/bar"} 1 2 3 4 5 1 2 3 4 5 + http_requests{path="/biz"} 0 0 0 0 0 1 1 1 1 1 + +# Test basic duration with unit: [30m] +eval instant at 50m changes(http_requests[30m]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test addition in duration: [26m+4m] +eval instant at 50m changes(http_requests[26m+4m]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test addition with 0 in duration: [30m+0s] +eval instant at 50m changes(http_requests[30m+0s]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test raw seconds: [1800] +eval instant at 50m changes(http_requests[1800]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test seconds with multiplication: [60*30] +eval instant at 50m changes(http_requests[60*30]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test minutes with multiplication: [2m*15] +eval instant at 50m changes(http_requests[2m*15]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test complex expression with parentheses: [2m*(10+5)] +eval instant at 50m changes(http_requests[2m*(10+5)]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test mixed units: [29m+60s] +eval instant at 50m changes(http_requests[29m+60s]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test nested parentheses: [24m+((1.5*2m)+2m)] +eval instant at 50m changes(http_requests[24m+((1.5*2m)+2m)]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test start with -: [-5m+35m] +eval instant at 50m changes(http_requests[-5m+35m]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test division: [1h/2] +eval instant at 50m changes(http_requests[1h/2]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test modulo: [1h30m % 1h] +eval instant at 50m changes(http_requests[1h30m % 1h]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test modulo and calculation: [30m1s-30m1s % 1m] +eval instant at 50m changes(http_requests[30m1s-30m1s % 1m]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +# Test combination of operations: [(9m30s+30s)*3] +eval instant at 50m changes(http_requests[(9m30s+30s)*3]) + {path="/foo"} 3 + {path="/bar"} 4 + {path="/biz"} 0 + +clear + +load 10s + metric1_total 0+1x1000 + +# In subquery expression. +eval instant at 1000s sum_over_time(metric1_total[29s+1s:5s+5s]) + {} 297 + +# Test complex expressions in subquery ranges. +eval instant at 1000s sum_over_time(metric1_total[29s+1s:((((8 - 2) / 3) * 7s) % 4) + 8000ms]) + {} 297 + +# Test complex expressions in offset ranges. +eval instant at 1200s sum_over_time(metric1_total[29s+1s:20*500ms] offset (20*(((((8 - 2) / 3) * 7s) % 4) + 8000ms))) + {} 297 + +# Test complex expressions in offset ranges with negative offset. +eval instant at 800s sum_over_time(metric1_total[29s+1s:20*500ms] offset -(20*(((((8 - 2) / 3) * 7s) % 4) + 8000ms))) + {} 297 + +# Test offset precedence with parentheses: offset (100 + 2) +eval instant at 1000s metric1_total offset (100 + 2) + {__name__="metric1_total"} 89 + +# Test offset precedence without parentheses: offset 100 + 2 +eval instant at 1000s metric1_total offset 100 + 2 + {} 92 \ No newline at end of file