Skip to main content

selene_gql/ast/
expr.rs

1//! Expression AST nodes.
2
3use std::sync::Arc;
4
5use rust_decimal::Decimal;
6use selene_core::DbString;
7
8use crate::ast::{
9    pattern::LabelExpr, pattern::MatchClause, span::SourceSpan, statement::QueryPipeline,
10    types::GqlType, util::NonEmpty,
11};
12
13/// Value expression.
14#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
15#[non_exhaustive]
16pub enum ValueExpr {
17    /// Literal value expression.
18    Literal(Literal),
19    /// Variable reference parsed from an identifier token.
20    Variable {
21        /// Database-string variable name.
22        name: DbString,
23        /// Source span of the variable reference.
24        span: SourceSpan,
25    },
26    /// Query parameter reference, such as `$name`.
27    Parameter {
28        /// Database-string parameter name without the leading `$`.
29        name: DbString,
30        /// Optional inline declared parameter type.
31        declared_type: Option<GqlType>,
32        /// Source span of the parameter reference.
33        span: SourceSpan,
34    },
35    /// Property access, such as `n.name`.
36    PropertyAccess {
37        /// Target expression.
38        target: Box<ValueExpr>,
39        /// Database-string property key.
40        key: DbString,
41        /// Source span of the full property access.
42        span: SourceSpan,
43    },
44    /// List literal.
45    ListLiteral {
46        /// Literal items.
47        items: Vec<ValueExpr>,
48        /// Source span of the list.
49        span: SourceSpan,
50    },
51    /// Record literal.
52    RecordLiteral {
53        /// Record fields in source order.
54        fields: Vec<(DbString, ValueExpr)>,
55        /// Source span of the record.
56        span: SourceSpan,
57    },
58    /// `PATH[<node>, <edge>, <node>, ...]` constructor.
59    PathConstructor {
60        /// Alternating node/edge/node reference expressions.
61        elements: Vec<ValueExpr>,
62        /// Source span of the path constructor.
63        span: SourceSpan,
64    },
65    /// Binary operator expression.
66    BinaryOp {
67        /// Operator.
68        op: BinaryOp,
69        /// Left operand.
70        lhs: Box<ValueExpr>,
71        /// Right operand.
72        rhs: Box<ValueExpr>,
73        /// Source span of the full expression.
74        span: SourceSpan,
75    },
76    /// Unary operator expression.
77    UnaryOp {
78        /// Operator.
79        op: UnaryOp,
80        /// Operand.
81        operand: Box<ValueExpr>,
82        /// Source span of the full expression.
83        span: SourceSpan,
84    },
85    /// Function call, including aggregate-looking calls.
86    FunctionCall {
87        /// Qualified function name as a list of database-string segments.
88        ///
89        /// Stored as a path so that `foo."bar.baz"` and `foo.bar.baz`
90        /// (which a flat string would alias) parse to distinguishable
91        /// values. Bare functions (`count(...)`) are a single-element
92        /// path; namespaced calls (`db.bar()`, `pkg.subpkg.fn()`) preserve
93        /// every segment.
94        name: NonEmpty<DbString>,
95        /// Function arguments.
96        args: Vec<ValueExpr>,
97        /// `true` for `count(*)`.
98        star: bool,
99        /// `true` when the call included `DISTINCT`.
100        distinct: bool,
101        /// Source span of the call.
102        span: SourceSpan,
103    },
104    /// `DURATION_BETWEEN(<start>, <end>) [<temporal duration qualifier>]`.
105    DurationBetween {
106        /// Start temporal value expression.
107        start: Box<ValueExpr>,
108        /// End temporal value expression.
109        end: Box<ValueExpr>,
110        /// Requested duration unit group.
111        qualifier: TemporalDurationQualifier,
112        /// Source span of the full expression.
113        span: SourceSpan,
114    },
115    /// `IS` predicate family.
116    IsCheck {
117        /// Checked operand.
118        operand: Box<ValueExpr>,
119        /// Predicate kind.
120        kind: IsCheckKind,
121        /// Whether the predicate was negated.
122        negated: bool,
123        /// Source span of the full predicate.
124        span: SourceSpan,
125    },
126    /// `[NOT] IN` predicate.
127    InList {
128        /// Checked operand.
129        operand: Box<ValueExpr>,
130        /// List values.
131        list: Vec<ValueExpr>,
132        /// Whether the predicate was negated.
133        negated: bool,
134        /// Source span of the full predicate.
135        span: SourceSpan,
136    },
137    /// `[NOT] IN` predicate with a list-valued expression on the right.
138    InListExpression {
139        /// Checked operand.
140        operand: Box<ValueExpr>,
141        /// List-valued expression.
142        list: Box<ValueExpr>,
143        /// Whether the predicate was negated.
144        negated: bool,
145        /// Source span of the full predicate.
146        span: SourceSpan,
147    },
148    /// `ALL_DIFFERENT(...)` predicate.
149    ///
150    /// ISO/IEC 39075:2024 section 19.11 takes element variable references.
151    /// The AST keeps a broad item shape so parser construction stays regular;
152    /// analysis enforces the ISO argument rule.
153    AllDifferent {
154        /// Items to compare.
155        items: Vec<ValueExpr>,
156        /// Source span of the predicate.
157        span: SourceSpan,
158    },
159    /// `SAME(...)` predicate.
160    ///
161    /// ISO/IEC 39075:2024 section 19.12 takes element variable references.
162    /// The AST keeps a broad item shape so parser construction stays regular;
163    /// analysis enforces the ISO argument rule.
164    Same {
165        /// Items to compare.
166        items: Vec<ValueExpr>,
167        /// Source span of the predicate.
168        span: SourceSpan,
169    },
170    /// `PROPERTY_EXISTS(target, 'key')` predicate.
171    ///
172    /// ISO/IEC 39075:2024 section 19.13 takes an element variable reference
173    /// and property name. The AST keeps a broad target shape so parser
174    /// construction stays regular; analysis enforces the ISO target rule.
175    PropertyExists {
176        /// Target expression.
177        target: Box<ValueExpr>,
178        /// Database-string property key.
179        key: DbString,
180        /// Source spelling class for the property-key character string literal.
181        key_source_kind: CharacterStringLiteralKind,
182        /// Source span of the predicate.
183        span: SourceSpan,
184    },
185    /// `CASE` expression.
186    Case {
187        /// `(condition, value)` branches.
188        branches: Vec<(ValueExpr, ValueExpr)>,
189        /// Optional else branch.
190        else_branch: Option<Box<ValueExpr>>,
191        /// Source span of the full expression.
192        span: SourceSpan,
193    },
194    /// `EXISTS { ... }` subquery predicate.
195    Exists {
196        /// Nested body.
197        body: ExistsBody,
198        /// Whether the predicate was negated.
199        negated: bool,
200        /// Source span of the full expression.
201        span: SourceSpan,
202    },
203    /// `VALUE { ... }` scalar value query expression.
204    ValueSubquery {
205        /// Nested query body.
206        body: Box<QueryPipeline>,
207        /// Source span of the full expression.
208        span: SourceSpan,
209    },
210    /// `NORMALIZE(<source>[, <normal form>])` Unicode normalization expression.
211    Normalize {
212        /// Source string expression.
213        source: Box<ValueExpr>,
214        /// Optional normalization form; omitted form defaults to NFC.
215        form: Option<NormalForm>,
216        /// Source span of the full expression.
217        span: SourceSpan,
218    },
219    /// Explicit `TRIM([LEADING|TRAILING|BOTH] [char] FROM source)` expression.
220    Trim {
221        /// Trim direction.
222        spec: TrimSpec,
223        /// Optional trim character expression; omitted character defaults to space.
224        character: Option<Box<ValueExpr>>,
225        /// Source string expression.
226        source: Box<ValueExpr>,
227        /// Source span of the full expression.
228        span: SourceSpan,
229    },
230    /// `CAST(<value> AS <target_type>)` explicit cast expression per ISO §22.
231    ///
232    /// `target_type` is boxed to keep the size of [`ValueExpr`] bounded; the
233    /// nested `GqlType::List(Box<GqlType>)` chain is otherwise heap-resident
234    /// already, and the analyzer-recursion test (`addition_statement(100)`)
235    /// stack-overflows if a non-boxed `GqlType` is added inline.
236    Cast {
237        /// Source expression being cast.
238        value: Box<ValueExpr>,
239        /// Declared target GQL type.
240        target_type: Box<GqlType>,
241        /// Source span of the full expression.
242        span: SourceSpan,
243    },
244}
245
246/// Nested body accepted by an `EXISTS` predicate.
247#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
248pub enum ExistsBody {
249    /// Existing single-MATCH body.
250    Match(Box<MatchClause>),
251    /// Full query pipeline body.
252    Query(Box<QueryPipeline>),
253}
254
255/// Temporal duration qualifier for `DURATION_BETWEEN`.
256#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
257pub enum TemporalDurationQualifier {
258    /// `YEAR TO MONTH`.
259    YearToMonth,
260    /// `DAY TO SECOND`.
261    DayToSecond,
262}
263
264impl Drop for ValueExpr {
265    /// Tear this expression down iteratively rather than with the compiler's
266    /// derived recursive destructor.
267    ///
268    /// A left-leaning `Box<ValueExpr>` chain (e.g. the depth-N tree built from
269    /// `a OR a OR …` or a long `a.b.c.…` access chain) overflows the native
270    /// stack when freed recursively — one frame per level — at roughly 130k
271    /// deep. A Rust stack overflow is **non-unwindable** (`catch_unwind` cannot
272    /// trap it), so it hard-kills the host process embedding selene-db. The
273    /// parser caps *accepted* expression depth far below that
274    /// (`parser::depth`), but an over-cap tree is still fully constructed before
275    /// the cap rejects it, and that rejected tree must be dropped without
276    /// recursing. This manual `Drop` hoists every owned child `ValueExpr` onto a
277    /// heap worklist (replacing it in place with a childless placeholder) and
278    /// drops the worklist in a pop-loop, so teardown is O(nodes) iterative.
279    ///
280    /// By the time any hoisted node is itself dropped, its children have already
281    /// been replaced by placeholders, so its re-entrant destructor finds nothing
282    /// to recurse into (recursion depth stays ≤ 2 regardless of tree depth).
283    /// Subquery bodies (`Box<MatchClause>` / `Box<QueryPipeline>`) and the
284    /// `Cast` target type (`Box<GqlType>`) are **not** direct `ValueExpr`
285    /// children (see [`ValueExpr::for_each_child_mut`]); they drop normally,
286    /// which is safe because subquery nesting is bounded by the pre-pest `{`
287    /// delimiter cap and `GqlType` depth by the type builder's gate.
288    fn drop(&mut self) {
289        let mut pending: Vec<ValueExpr> = Vec::new();
290        hoist_children(self, &mut pending);
291        while let Some(mut node) = pending.pop() {
292            hoist_children(&mut node, &mut pending);
293            // `node` drops here with its children already hoisted out.
294        }
295    }
296}
297
298/// Move every direct child [`ValueExpr`] of `expr` onto `pending`, leaving a
299/// childless placeholder in its slot. Shared by the iterative [`Drop`] impl.
300fn hoist_children(expr: &mut ValueExpr, pending: &mut Vec<ValueExpr>) {
301    expr.for_each_child_mut(&mut |child| {
302        pending.push(core::mem::replace(child, drop_placeholder()));
303    });
304}
305
306/// A cheap, childless [`ValueExpr`] swapped in for a hoisted child during
307/// iterative teardown. Constructing it allocates nothing and dropping it
308/// recurses into nothing.
309fn drop_placeholder() -> ValueExpr {
310    ValueExpr::Literal(Literal::Null(SourceSpan::default()))
311}
312
313impl ValueExpr {
314    /// Return the source span for this expression.
315    #[must_use]
316    pub const fn span(&self) -> SourceSpan {
317        match self {
318            Self::Literal(literal) => literal.span(),
319            Self::Variable { span, .. }
320            | Self::Parameter { span, .. }
321            | Self::PropertyAccess { span, .. }
322            | Self::ListLiteral { span, .. }
323            | Self::RecordLiteral { span, .. }
324            | Self::PathConstructor { span, .. }
325            | Self::BinaryOp { span, .. }
326            | Self::UnaryOp { span, .. }
327            | Self::FunctionCall { span, .. }
328            | Self::DurationBetween { span, .. }
329            | Self::IsCheck { span, .. }
330            | Self::InList { span, .. }
331            | Self::InListExpression { span, .. }
332            | Self::AllDifferent { span, .. }
333            | Self::Same { span, .. }
334            | Self::PropertyExists { span, .. }
335            | Self::Case { span, .. }
336            | Self::Exists { span, .. }
337            | Self::ValueSubquery { span, .. }
338            | Self::Normalize { span, .. }
339            | Self::Trim { span, .. }
340            | Self::Cast { span, .. } => *span,
341        }
342    }
343}
344
345/// Binary operator.
346#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
347pub enum BinaryOp {
348    /// Addition.
349    Add,
350    /// Subtraction.
351    Sub,
352    /// Multiplication.
353    Mul,
354    /// Division.
355    Div,
356    /// Remainder.
357    Mod,
358    /// Exponentiation.
359    ///
360    /// selene-db extension: ISO/IEC 39075:2024 section 20.22 uses `POWER(x, y)`;
361    /// the grammar does not emit `^` today.
362    Power,
363    /// Equality.
364    Eq,
365    /// Inequality.
366    Ne,
367    /// Less-than.
368    Lt,
369    /// Less-than-or-equal.
370    Le,
371    /// Greater-than.
372    Gt,
373    /// Greater-than-or-equal.
374    Ge,
375    /// Boolean conjunction.
376    And,
377    /// Boolean disjunction.
378    Or,
379    /// Boolean exclusive-or.
380    Xor,
381    /// String/list/bytes/path concatenation.
382    Concat,
383    /// `CONTAINS`.
384    Contains,
385    /// `STARTS WITH`.
386    StartsWith,
387    /// `ENDS WITH`.
388    EndsWith,
389}
390
391/// Unary operator.
392#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
393pub enum UnaryOp {
394    /// Numeric negation.
395    Negate,
396    /// Boolean negation.
397    Not,
398}
399
400/// `IS` predicate kind.
401#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
402#[non_exhaustive]
403pub enum IsCheckKind {
404    /// `IS NULL`.
405    Null,
406    /// `IS DIRECTED`.
407    Directed,
408    /// `IS LABELED`.
409    Labeled(LabelExpr),
410    /// `IS TRUE/FALSE/UNKNOWN`.
411    TruthValue(TruthValue),
412    /// `IS TYPED`.
413    Typed(GqlType),
414    /// `IS NORMALIZED`.
415    Normalized(NormalForm),
416    /// `IS SOURCE OF`.
417    SourceOf(Box<ValueExpr>),
418    /// `IS DESTINATION OF`.
419    DestinationOf(Box<ValueExpr>),
420}
421
422/// Three-valued boolean predicate target.
423#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
424pub enum TruthValue {
425    /// `TRUE`.
426    True,
427    /// `FALSE`.
428    False,
429    /// `UNKNOWN`.
430    Unknown,
431}
432
433/// Unicode normalization form.
434#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
435pub enum NormalForm {
436    /// NFC.
437    Nfc,
438    /// NFD.
439    Nfd,
440    /// NFKC.
441    Nfkc,
442    /// NFKD.
443    Nfkd,
444}
445
446/// Explicit TRIM direction.
447#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
448pub enum TrimSpec {
449    /// Trim leading characters.
450    Leading,
451    /// Trim trailing characters.
452    Trailing,
453    /// Trim both leading and trailing characters.
454    Both,
455}
456
457/// Literal expression.
458#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
459#[non_exhaustive]
460pub enum Literal {
461    /// Boolean literal.
462    Bool(bool, SourceSpan),
463    /// Signed 64-bit integer literal.
464    Integer(i64, SourceSpan),
465    /// Signed 64-bit integer literal written with a non-decimal radix prefix.
466    RadixInteger(i64, SourceSpan, IntegerLiteralKind),
467    /// Exact fixed-precision decimal literal.
468    Decimal(Decimal, SourceSpan, DecimalLiteralKind),
469    /// 64-bit floating-point literal.
470    Float(f64, SourceSpan, FloatLiteralKind),
471    /// Database-string literal.
472    String(DbString, SourceSpan, CharacterStringLiteralKind),
473    /// Byte-string literal.
474    Bytes(Arc<[u8]>, SourceSpan),
475    /// UUID literal.
476    Uuid(uuid::Uuid, SourceSpan, CharacterStringLiteralKind),
477    /// Zoned datetime literal.
478    ZonedDateTime(Box<jiff::Zoned>, SourceSpan, CharacterStringLiteralKind),
479    /// Local datetime literal.
480    LocalDateTime(
481        jiff::civil::DateTime,
482        SourceSpan,
483        CharacterStringLiteralKind,
484    ),
485    /// Date literal.
486    Date(jiff::civil::Date, SourceSpan, CharacterStringLiteralKind),
487    /// Zoned time literal.
488    ZonedTime(Box<jiff::Zoned>, SourceSpan, CharacterStringLiteralKind),
489    /// Local time literal.
490    LocalTime(jiff::civil::Time, SourceSpan, CharacterStringLiteralKind),
491    /// Duration literal.
492    Duration(Box<jiff::Span>, SourceSpan, CharacterStringLiteralKind),
493    /// Null literal.
494    Null(SourceSpan),
495}
496
497/// Source spelling class for an ISO character string literal.
498#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
499pub enum CharacterStringLiteralKind {
500    /// Standard character string literal with character escaping enabled.
501    Escaped,
502    /// Character string literal prefixed by `<no escape>` (`@`), disabling
503    /// backslash and doubled-delimiter character representations.
504    NoEscape,
505}
506
507/// Source spelling class for an integer literal.
508#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
509pub enum IntegerLiteralKind {
510    /// Hexadecimal literal with a `0x` prefix.
511    Hexadecimal,
512    /// Octal literal with a `0o` prefix.
513    Octal,
514    /// Binary literal with a `0b` prefix.
515    Binary,
516}
517
518/// Source spelling class for an exact decimal literal.
519#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
520pub enum DecimalLiteralKind {
521    /// Common decimal notation without an exact-number suffix.
522    CommonWithoutSuffix,
523    /// Common decimal notation or decimal integer with an exact-number suffix.
524    CommonOrIntegerWithSuffix,
525    /// Scientific decimal notation with an exact-number suffix.
526    ScientificWithSuffix,
527}
528
529/// Source spelling class for an approximate numeric literal.
530#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
531pub enum FloatLiteralKind {
532    /// Scientific decimal notation without an approximate-number suffix.
533    ScientificWithoutSuffix,
534    /// Common decimal notation or decimal integer with an `F` suffix.
535    CommonOrIntegerWithFloatSuffix,
536    /// Common decimal notation or decimal integer with a `D` suffix.
537    CommonOrIntegerWithDoubleSuffix,
538    /// Scientific decimal notation with an `F` suffix.
539    ScientificWithFloatSuffix,
540    /// Scientific decimal notation with a `D` suffix.
541    ScientificWithDoubleSuffix,
542}
543
544impl PartialEq for Literal {
545    fn eq(&self, rhs: &Self) -> bool {
546        match (self, rhs) {
547            (Self::Bool(lhs, lhs_span), Self::Bool(rhs, rhs_span)) => {
548                lhs == rhs && lhs_span == rhs_span
549            }
550            (Self::Integer(lhs, lhs_span), Self::Integer(rhs, rhs_span)) => {
551                lhs == rhs && lhs_span == rhs_span
552            }
553            (
554                Self::RadixInteger(lhs, lhs_span, lhs_kind),
555                Self::RadixInteger(rhs, rhs_span, rhs_kind),
556            ) => lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind,
557            (Self::Decimal(lhs, lhs_span, lhs_kind), Self::Decimal(rhs, rhs_span, rhs_kind)) => {
558                lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind
559            }
560            (Self::Float(lhs, lhs_span, lhs_kind), Self::Float(rhs, rhs_span, rhs_kind)) => {
561                lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind
562            }
563            (Self::String(lhs, lhs_span, lhs_kind), Self::String(rhs, rhs_span, rhs_kind)) => {
564                lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind
565            }
566            (Self::Bytes(lhs, lhs_span), Self::Bytes(rhs, rhs_span)) => {
567                lhs == rhs && lhs_span == rhs_span
568            }
569            (Self::Uuid(lhs, lhs_span, lhs_kind), Self::Uuid(rhs, rhs_span, rhs_kind)) => {
570                lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind
571            }
572            (
573                Self::ZonedDateTime(lhs, lhs_span, lhs_kind),
574                Self::ZonedDateTime(rhs, rhs_span, rhs_kind),
575            ) => lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind,
576            (
577                Self::LocalDateTime(lhs, lhs_span, lhs_kind),
578                Self::LocalDateTime(rhs, rhs_span, rhs_kind),
579            ) => lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind,
580            (Self::Date(lhs, lhs_span, lhs_kind), Self::Date(rhs, rhs_span, rhs_kind)) => {
581                lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind
582            }
583            (
584                Self::ZonedTime(lhs, lhs_span, lhs_kind),
585                Self::ZonedTime(rhs, rhs_span, rhs_kind),
586            ) => lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind,
587            (
588                Self::LocalTime(lhs, lhs_span, lhs_kind),
589                Self::LocalTime(rhs, rhs_span, rhs_kind),
590            ) => lhs == rhs && lhs_span == rhs_span && lhs_kind == rhs_kind,
591            (Self::Duration(lhs, lhs_span, lhs_kind), Self::Duration(rhs, rhs_span, rhs_kind)) => {
592                lhs.fieldwise() == rhs.fieldwise() && lhs_span == rhs_span && lhs_kind == rhs_kind
593            }
594            (Self::Null(lhs_span), Self::Null(rhs_span)) => lhs_span == rhs_span,
595            _ => false,
596        }
597    }
598}
599
600impl Literal {
601    /// Return the source span for this literal.
602    #[must_use]
603    pub const fn span(&self) -> SourceSpan {
604        match self {
605            Self::Bool(_, span)
606            | Self::Integer(_, span)
607            | Self::RadixInteger(_, span, _)
608            | Self::Decimal(_, span, _)
609            | Self::Float(_, span, _)
610            | Self::String(_, span, _)
611            | Self::Bytes(_, span)
612            | Self::Uuid(_, span, _)
613            | Self::ZonedDateTime(_, span, _)
614            | Self::LocalDateTime(_, span, _)
615            | Self::Date(_, span, _)
616            | Self::ZonedTime(_, span, _)
617            | Self::LocalTime(_, span, _)
618            | Self::Duration(_, span, _)
619            | Self::Null(span) => *span,
620        }
621    }
622
623    /// Return a mutable reference to this literal's source span.
624    ///
625    /// Used by [`ValueExpr::for_each_span_mut`](crate::ValueExpr::for_each_span_mut)
626    /// so span-rewriting walkers (parser span rebasing, span-erasing equality)
627    /// can reach the span stored inside the literal variant.
628    pub const fn span_mut(&mut self) -> &mut SourceSpan {
629        match self {
630            Self::Bool(_, span)
631            | Self::Integer(_, span)
632            | Self::RadixInteger(_, span, _)
633            | Self::Decimal(_, span, _)
634            | Self::Float(_, span, _)
635            | Self::String(_, span, _)
636            | Self::Bytes(_, span)
637            | Self::Uuid(_, span, _)
638            | Self::ZonedDateTime(_, span, _)
639            | Self::LocalDateTime(_, span, _)
640            | Self::Date(_, span, _)
641            | Self::ZonedTime(_, span, _)
642            | Self::LocalTime(_, span, _)
643            | Self::Duration(_, span, _)
644            | Self::Null(span) => span,
645        }
646    }
647}