///|
/// One typed regex expression. Composite nodes are flat: a concatenation
/// never holds another concatenation directly, and an alternation never
/// holds another alternation directly.
pub(all) enum Expr {
/// One code point. `true` marks an escaped character such as `\.`.
Char(String, Bool, @diagrams.Span?)
Class(@model.ClassKind, @diagrams.Span?)
Backreference(String, @diagrams.Span?)
/// A bracketed set. `true` marks a negated set.
Set(Bool, ReadOnlyArray[@model.SetItem], @diagrams.Span?)
Anchor(@model.AnchorKind, @diagrams.Span?)
Concat(ReadOnlyArray[Expr])
Alt(ReadOnlyArray[Expr])
Group(@model.GroupKind, Expr, @diagrams.Span?)
/// A lookaround. `true` marks a positive assertion.
Look(@model.Look, Bool, Expr, @diagrams.Span?)
Quantified(Expr, @model.Bounds, @diagrams.Span?)
} derive(Eq, Debug)
///|
pub extend Expr with Eq::{not_equal, equal}
///|
pub extend Expr with @debug.Debug::{to_repr}
///|
/// The expression without source locations, for logical comparison.
pub fn Expr::without_spans(self : Expr) -> Expr {
match self {
Char(c, escaped, _) => Char(c, escaped, None)
Class(kind, _) => Class(kind, None)
Backreference(n, _) => Backreference(n, None)
Set(negated, items, _) => Set(negated, items, None)
Anchor(kind, _) => Anchor(kind, None)
Concat(items) => Concat(ReadOnlyArray::from_iter(items.iter().map(e => e.without_spans())))
Alt(items) => Alt(ReadOnlyArray::from_iter(items.iter().map(e => e.without_spans())))
Group(kind, inner, _) => Group(kind, inner.without_spans(), None)
Look(look, positive, inner, _) =>
Look(look, positive, inner.without_spans(), None)
Quantified(inner, bounds, _) => Quantified(inner.without_spans(), bounds, None)
}
}
///|
/// One parsed regex block.
pub struct Document {
name : String?
statements : ReadOnlyArray[@envelope.Statement]
pattern : Expr
/// The source spelling of the pattern line, for display.
text : String
span : @diagrams.Span?
} derive(Eq, Debug)
///|
pub extend Document with Eq::{not_equal, equal}
///|
pub extend Document with @debug.Debug::{to_repr}
///|
pub fn Document::new(
pattern : Expr,
name? : String,
statements? : ArrayView[@envelope.Statement] = [],
text? : String = "",
) -> Document {
{
name,
statements: ReadOnlyArray::from_array(statements),
pattern,
text,
span: None,
}
}
///|
/// The document without source locations and source spelling.
pub fn Document::without_spans(self : Document) -> Document {
{ ..self, pattern: self.pattern.without_spans(), text: "", span: None }
}
///|
/// True when two documents have equal logical content. Source locations,
/// source spelling and trivia are not compared.
pub fn Document::logical_equal(self : Document, other : Document) -> Bool {
self.without_spans() == other.without_spans()
}