From 7b29b912e3712cb06e93d3c2db7f77cc2b4fee96 Mon Sep 17 00:00:00 2001
From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Date: Wed, 13 May 2026 09:29:32 +0200
Subject: [PATCH 1/3] Revert "PromQL: Promote duration expressions as stable"
This reverts commit 1463a5bb5a6f837e56f83d83f9f0726804a62e56.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
---
cmd/prometheus/main.go | 5 +-
cmd/prometheus/testdata/features.json | 2 +-
cmd/promtool/main.go | 4 +-
docs/command-line/prometheus.md | 2 +-
docs/command-line/promtool.md | 2 +-
docs/feature_flags.md | 57 +++++++++++++++
docs/querying/basics.md | 69 +++----------------
docs/querying/functions.md | 3 +-
promql/durations_test.go | 2 +-
promql/parser/features.go | 4 +-
promql/parser/generated_parser.y | 7 ++
promql/parser/generated_parser.y.go | 7 ++
promql/parser/parse.go | 7 ++
promql/parser/parse_test.go | 2 +
promql/parser/prettier_test.go | 2 +-
promql/parser/printer_test.go | 1 +
promql/promqltest/test.go | 1 +
util/fuzzing/fuzz_test.go | 1 +
web/ui/mantine-ui/src/promql/functionDocs.tsx | 48 ++++++++-----
19 files changed, 138 insertions(+), 88 deletions(-)
diff --git a/cmd/prometheus/main.go b/cmd/prometheus/main.go
index fcb655fb1e..6066a5be1d 100644
--- a/cmd/prometheus/main.go
+++ b/cmd/prometheus/main.go
@@ -260,7 +260,8 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error {
c.parserOpts.EnableExperimentalFunctions = true
logger.Info("Experimental PromQL functions enabled.")
case "promql-duration-expr":
- logger.Warn("This option for --enable-feature is now permanently enabled and therefore a no-op.", "option", o)
+ c.parserOpts.ExperimentalDurationExpr = true
+ logger.Info("Experimental duration expression parsing enabled.")
case "native-histograms":
logger.Warn("This option for --enable-feature is a no-op. To scrape native histograms, set the scrape_native_histograms scrape config setting to true.", "option", o)
case "ooo-native-histograms":
@@ -619,7 +620,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: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
+ a.Flag("enable-feature", "Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details.").
StringsVar(&cfg.featureList)
a.Flag("agent", "Run Prometheus in 'Agent mode'.").BoolVar(&agentMode)
diff --git a/cmd/prometheus/testdata/features.json b/cmd/prometheus/testdata/features.json
index 90bf375781..78c6b5bdc8 100644
--- a/cmd/prometheus/testdata/features.json
+++ b/cmd/prometheus/testdata/features.json
@@ -29,7 +29,7 @@
"bool": true,
"by": true,
"delayed_name_removal": false,
- "duration_expr": true,
+ "duration_expr": false,
"fill": false,
"fill_left": false,
"fill_right": false,
diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go
index 88ee3be3f7..fbea5eda6a 100644
--- a/cmd/promtool/main.go
+++ b/cmd/promtool/main.go
@@ -320,7 +320,7 @@ func main() {
promQLLabelsDeleteQuery := promQLLabelsDeleteCmd.Arg("query", "PromQL query.").Required().String()
promQLLabelsDeleteName := promQLLabelsDeleteCmd.Arg("name", "Name of the label to delete.").Required().String()
- featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings()
+ featureList := app.Flag("enable-feature", "Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details").Default("").Strings()
documentationCmd := app.Command("write-documentation", "Generate command line documentation. Internal use.").Hidden()
@@ -358,7 +358,7 @@ func main() {
case "promql-delayed-name-removal":
promqlEnableDelayedNameRemoval = true
case "promql-duration-expr":
- fmt.Printf(" WARNING: promql-duration-expr is now permanently enabled and therefore a no-op")
+ promtoolParserOpts.ExperimentalDurationExpr = true
case "promql-extended-range-selectors":
promtoolParserOpts.EnableExtendedRangeSelectors = true
case "":
diff --git a/docs/command-line/prometheus.md b/docs/command-line/prometheus.md
index e21c4c2a67..289ded3842 100644
--- a/docs/command-line/prometheus.md
+++ b/docs/command-line/prometheus.md
@@ -62,7 +62,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: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details. | |
+| --enable-feature ... | Comma separated feature names to enable. Valid options: concurrent-rule-eval, created-timestamp-zero-ingestion, delayed-compaction, exemplar-storage, extra-scrape-metrics, memory-snapshot-on-shutdown, metadata-wal-records, old-ui, otlp-deltatocumulative, otlp-native-delta-ingestion, promql-binop-fill-modifiers, promql-delayed-name-removal, promql-duration-expr, promql-experimental-functions, promql-extended-range-selectors, promql-per-step-stats, st-storage, type-and-unit-labels, use-start-timestamps, use-uncached-io, xor2-encoding. 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/command-line/promtool.md b/docs/command-line/promtool.md
index aa606435f8..28a0d99696 100644
--- a/docs/command-line/promtool.md
+++ b/docs/command-line/promtool.md
@@ -12,7 +12,7 @@ Tooling for the Prometheus monitoring system.
| -h, --help | Show context-sensitive help (also try --help-long and --help-man). |
| --version | Show application version. |
| --experimental | Enable experimental commands. |
-| --enable-feature ... | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details |
+| --enable-feature ... | Comma separated feature names to enable. Valid options: promql-experimental-functions, promql-delayed-name-removal, promql-duration-expr, promql-extended-range-selectors. See https://prometheus.io/docs/prometheus/latest/feature_flags/ for more details |
diff --git a/docs/feature_flags.md b/docs/feature_flags.md
index fddb4dcf6b..2806e4baa7 100644
--- a/docs/feature_flags.md
+++ b/docs/feature_flags.md
@@ -192,6 +192,63 @@ state is mutex guarded. Cumulative-only OTLP requests are not affected.
[d2c]: https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/deltatocumulativeprocessor
+## PromQL arithmetic expressions in time durations
+
+`--enable-feature=promql-duration-expr`
+
+With this flag, arithmetic expressions can be used in time durations in range queries and offset durations.
+
+In range queries:
+```
+rate(http_requests_total[5m * 2]) # 10 minute range
+rate(http_requests_total[(5+2) * 1m]) # 7 minute range
+```
+
+In offset durations:
+```
+http_requests_total offset (1h / 2) # 30 minute offset
+http_requests_total offset ((2 ^ 3) * 1m) # 8 minute offset
+```
+
+When using offset with duration expressions, you must wrap the expression in
+parentheses. Without parentheses, only the first duration value will be used in
+the offset calculation.
+
+`step()` can be used in duration expressions.
+For a **range query**, it resolves to the step width of the range query.
+For an **instant query**, it resolves to `0s`.
+
+`range()` can be used in duration expressions.
+For a **range query**, it resolves to the full range of the query (end time - start time).
+For an **instant query**, it resolves to `0s`.
+This is particularly useful in combination with `@end()` to look back over the entire query range, e.g., `max_over_time(metric[range()] @ end())`.
+
+`min(m within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
m within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -1238,7 +1240,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2069,7 +2072,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2219,7 +2223,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2328,7 +2333,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2437,7 +2443,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2652,7 +2659,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -2761,7 +2769,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3274,7 +3283,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3383,7 +3393,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3508,7 +3519,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3773,7 +3785,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3882,7 +3895,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -3991,7 +4005,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
@@ -4100,7 +4115,8 @@ const funcDocs: Recordm within the 1m range, where m offset 1m will select the most
recent sample within the lookback interval outside and prior to the 1m offset. This is particularly
useful with first_over_time(m[step()])
- in range queries to ensure that the sample selected is within the range step.
+ in range queries (available when --enable-feature=promql-duration-expr is set) to ensure that the
+ sample selected is within the range step.
>
),
From 502e31a82fe42ec56de76f658c303b1ab7a91a00 Mon Sep 17 00:00:00 2001
From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Date: Wed, 13 May 2026 09:56:54 +0200
Subject: [PATCH 2/3] promql: protect min(), max(), step(), and range()
duration exprs with feature flag
min(), max(), step(), and range() in duration expression context were
not guarded by the ExperimentalDurationExpr feature flag, unlike the
binary operators (+, -, *, /, %, ^). Add the missing
experimentalDurationExpr() calls in both offset_duration_expr and
duration_expr grammar rules.
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
---
promql/parser/generated_parser.y | 44 ++++++++++++++++++++---------
promql/parser/generated_parser.y.go | 36 +++++++++++++++++------
2 files changed, 58 insertions(+), 22 deletions(-)
diff --git a/promql/parser/generated_parser.y b/promql/parser/generated_parser.y
index 39dfa1f49f..34ce028c1f 100644
--- a/promql/parser/generated_parser.y
+++ b/promql/parser/generated_parser.y
@@ -1201,23 +1201,27 @@ offset_duration_expr : number_duration_literal
}
| STEP LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
- Op: STEP,
+ de := &DurationExpr{
+ Op: STEP,
StartPos: $1.PositionRange().Start,
- EndPos: $3.PositionRange().End,
+ EndPos: $3.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| RANGE LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
- Op: RANGE,
+ de := &DurationExpr{
+ Op: RANGE,
StartPos: $1.PositionRange().Start,
- EndPos: $3.PositionRange().End,
+ EndPos: $3.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| unary_op STEP LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: $1.Typ,
RHS: &DurationExpr{
Op: STEP,
@@ -1226,10 +1230,12 @@ offset_duration_expr : number_duration_literal
},
StartPos: $1.Pos,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| unary_op RANGE LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: $1.Typ,
RHS: &DurationExpr{
Op: RANGE,
@@ -1238,20 +1244,24 @@ offset_duration_expr : number_duration_literal
},
StartPos: $1.Pos,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.PositionRange().Start,
EndPos: $6.PositionRange().End,
LHS: $3.(Expr),
RHS: $5.(Expr),
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| unary_op min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.Pos,
EndPos: $6.PositionRange().End,
@@ -1263,6 +1273,8 @@ offset_duration_expr : number_duration_literal
RHS: $6.(Expr),
},
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| unary_op LEFT_PAREN duration_expr RIGHT_PAREN %prec MUL
{
@@ -1367,29 +1379,35 @@ duration_expr : number_duration_literal
}
| STEP LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: STEP,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| RANGE LEFT_PAREN RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: RANGE,
StartPos: $1.PositionRange().Start,
EndPos: $3.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| min_max LEFT_PAREN duration_expr COMMA duration_expr RIGHT_PAREN
{
- $$ = &DurationExpr{
+ de := &DurationExpr{
Op: $1.Typ,
StartPos: $1.PositionRange().Start,
EndPos: $6.PositionRange().End,
LHS: $3.(Expr),
RHS: $5.(Expr),
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ $$ = de
}
| paren_duration_expr
;
diff --git a/promql/parser/generated_parser.y.go b/promql/parser/generated_parser.y.go
index bf96f73f5a..9f9f017dfa 100644
--- a/promql/parser/generated_parser.y.go
+++ b/promql/parser/generated_parser.y.go
@@ -2316,25 +2316,29 @@ yydefault:
case 282:
yyDollar = yyS[yypt-3 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: STEP,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 283:
yyDollar = yyS[yypt-3 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: RANGE,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 284:
yyDollar = yyS[yypt-4 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: yyDollar[1].item.Typ,
RHS: &DurationExpr{
Op: STEP,
@@ -2343,11 +2347,13 @@ yydefault:
},
StartPos: yyDollar[1].item.Pos,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 285:
yyDollar = yyS[yypt-4 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: yyDollar[1].item.Typ,
RHS: &DurationExpr{
Op: RANGE,
@@ -2356,22 +2362,26 @@ yydefault:
},
StartPos: yyDollar[1].item.Pos,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 286:
yyDollar = yyS[yypt-6 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[6].item.PositionRange().End,
LHS: yyDollar[3].node.(Expr),
RHS: yyDollar[5].node.(Expr),
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 287:
yyDollar = yyS[yypt-7 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.Pos,
EndPos: yyDollar[6].node.PositionRange().End,
@@ -2383,6 +2393,8 @@ yydefault:
RHS: yyDollar[6].node.(Expr),
},
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 288:
yyDollar = yyS[yypt-4 : yypt+1]
@@ -2492,31 +2504,37 @@ yydefault:
case 300:
yyDollar = yyS[yypt-3 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: STEP,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 301:
yyDollar = yyS[yypt-3 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: RANGE,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[3].item.PositionRange().End,
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 302:
yyDollar = yyS[yypt-6 : yypt+1]
{
- yyVAL.node = &DurationExpr{
+ de := &DurationExpr{
Op: yyDollar[1].item.Typ,
StartPos: yyDollar[1].item.PositionRange().Start,
EndPos: yyDollar[6].item.PositionRange().End,
LHS: yyDollar[3].node.(Expr),
RHS: yyDollar[5].node.(Expr),
}
+ yylex.(*parser).experimentalDurationExpr(de)
+ yyVAL.node = de
}
case 304:
yyDollar = yyS[yypt-3 : yypt+1]
From dd406b3df5002cf67a298b73928ec7435024102f Mon Sep 17 00:00:00 2001
From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
Date: Wed, 13 May 2026 10:52:59 +0200
Subject: [PATCH 3/3] web/api/v1: enable ExperimentalDurationExpr in translate
AST tests
Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com>
---
web/api/v1/translate_ast_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/web/api/v1/translate_ast_test.go b/web/api/v1/translate_ast_test.go
index 50befb1962..84c4019363 100644
--- a/web/api/v1/translate_ast_test.go
+++ b/web/api/v1/translate_ast_test.go
@@ -22,7 +22,7 @@ import (
)
func TestTranslateASTDurationExpressions(t *testing.T) {
- p := parser.NewParser(parser.Options{})
+ p := parser.NewParser(parser.Options{ExperimentalDurationExpr: true})
type tc struct {
name string