command: Operand carries a source span
Each WHERE-expression Operand now records the byte span of the terminal it was built from — the precise per-literal highlight target for an expression WARNING (finishing ADR-0027 §2's highlight/hint wiring). parse_operand captures MatchedItem::span; the RowFilter::eq convenience constructor uses Operand::NO_SPAN. PartialEq is hand-written to ignore the span — it is editor metadata, so Command equality stays whitespace- and position-independent, which the Expr test corpus relies on. No behaviour change; 1100 tests still pass, clippy clean.
This commit is contained in:
+49
-5
@@ -257,9 +257,15 @@ impl RowFilter {
|
||||
#[must_use]
|
||||
pub fn eq(column: impl Into<String>, value: Value) -> Self {
|
||||
Self::Where(Expr::Predicate(Predicate::Compare {
|
||||
left: Operand::Column(column.into()),
|
||||
left: Operand::Column {
|
||||
name: column.into(),
|
||||
span: Operand::NO_SPAN,
|
||||
},
|
||||
op: CompareOp::Eq,
|
||||
right: Operand::Literal(value),
|
||||
right: Operand::Literal {
|
||||
value,
|
||||
span: Operand::NO_SPAN,
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -322,10 +328,48 @@ pub enum Predicate {
|
||||
|
||||
/// A comparison operand — a column reference or a literal
|
||||
/// (ADR-0026 §1: operands are never nested expressions).
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
///
|
||||
/// Each operand carries the byte `span` it occupied in the
|
||||
/// source. The span feeds the precise per-literal WARNING
|
||||
/// highlight (ADR-0027) and is otherwise editor metadata —
|
||||
/// `PartialEq` is hand-written to **ignore** it, so two
|
||||
/// operands are equal when their column / value match
|
||||
/// regardless of where they were typed. This keeps `Command`
|
||||
/// equality whitespace- and position-independent (the bulk of
|
||||
/// the `Expr` test corpus relies on it).
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub enum Operand {
|
||||
Column(String),
|
||||
Literal(Value),
|
||||
Column { name: String, span: (usize, usize) },
|
||||
Literal { value: Value, span: (usize, usize) },
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
/// Span used for operands built without a source position
|
||||
/// (the [`RowFilter::eq`] convenience constructor).
|
||||
pub const NO_SPAN: (usize, usize) = (0, 0);
|
||||
|
||||
/// The byte range this operand occupied in the source —
|
||||
/// [`Operand::NO_SPAN`] for programmatically-built operands.
|
||||
#[must_use]
|
||||
pub const fn span(&self) -> (usize, usize) {
|
||||
match self {
|
||||
Self::Column { span, .. } | Self::Literal { span, .. } => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Operand {
|
||||
/// Compares column name / literal value only — the source
|
||||
/// `span` is deliberately excluded (see the type docs).
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Self::Column { name: a, .. }, Self::Column { name: b, .. }) => a == b,
|
||||
(Self::Literal { value: a, .. }, Self::Literal { value: b, .. }) => {
|
||||
a == b
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The six comparison operators. `<>` and `!=` both parse to
|
||||
|
||||
Reference in New Issue
Block a user