Skip to main content

sim_lib_pattern/
domain_execute.rs

1//! Typed adapters from concrete text domains to the regular pattern executor.
2
3use crate::{
4    Automaton, ByteDomain, ByteOffset, CaptureId, CodeUnitDomain, CodeUnitOffset, ExecutionLimit,
5    ExecutionOutcome, ExecutionReceipt, ScalarDomain, ScalarOffset, SymbolDomain, TextLimits,
6    UnsupportedFeature, execute_regular,
7};
8use sim_text::CodeUnitString;
9use std::collections::BTreeMap;
10
11/// A half-open capture span whose offset type identifies its subject domain.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub struct DomainCaptureSpan<D: SymbolDomain> {
14    /// Inclusive start offset.
15    pub start: D::Offset,
16    /// Exclusive end offset.
17    pub end: D::Offset,
18}
19
20/// A successful match whose offsets cannot be mixed with another domain.
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct DomainMatch<D: SymbolDomain> {
23    /// Inclusive start offset.
24    pub start: D::Offset,
25    /// Exclusive end offset.
26    pub end: D::Offset,
27    /// Captures keyed by their stable compiled identifier.
28    pub captures: BTreeMap<CaptureId, DomainCaptureSpan<D>>,
29}
30
31/// A resource-accounted execution result with domain-typed match positions.
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub enum DomainExecutionOutcome<D: SymbolDomain> {
34    /// The automaton accepted a subject prefix.
35    Match {
36        /// Match and captures in the selected offset domain.
37        matched: DomainMatch<D>,
38        /// Consumed work.
39        receipt: ExecutionReceipt,
40    },
41    /// The automaton definitively rejected the subject.
42    NoMatch {
43        /// Consumed work.
44        receipt: ExecutionReceipt,
45    },
46    /// A configured resource boundary stopped execution.
47    Limit {
48        /// Exhausted resource.
49        limit: ExecutionLimit,
50        /// Work consumed before stopping.
51        receipt: ExecutionReceipt,
52    },
53    /// The construct belongs to the separately budgeted extension lane.
54    Unsupported {
55        /// Exact unsupported construct.
56        feature: UnsupportedFeature,
57        /// Regular work consumed before discovering it.
58        receipt: ExecutionReceipt,
59    },
60}
61
62trait IndexedDomain: SymbolDomain {
63    fn offset(index: usize) -> Self::Offset;
64}
65
66impl IndexedDomain for ByteDomain {
67    fn offset(index: usize) -> Self::Offset {
68        ByteOffset(index)
69    }
70}
71
72impl IndexedDomain for ScalarDomain {
73    fn offset(index: usize) -> Self::Offset {
74        ScalarOffset::new(index)
75    }
76}
77
78impl IndexedDomain for CodeUnitDomain {
79    fn offset(index: usize) -> Self::Offset {
80        CodeUnitOffset::new(index)
81    }
82}
83
84fn typed<D: IndexedDomain>(outcome: ExecutionOutcome) -> DomainExecutionOutcome<D> {
85    match outcome {
86        ExecutionOutcome::Match { matched, receipt } => DomainExecutionOutcome::Match {
87            matched: DomainMatch {
88                start: D::offset(matched.start),
89                end: D::offset(matched.end),
90                captures: matched
91                    .captures
92                    .into_iter()
93                    .map(|(id, span)| {
94                        (
95                            id,
96                            DomainCaptureSpan {
97                                start: D::offset(span.start),
98                                end: D::offset(span.end),
99                            },
100                        )
101                    })
102                    .collect(),
103            },
104            receipt,
105        },
106        ExecutionOutcome::NoMatch { receipt } => DomainExecutionOutcome::NoMatch { receipt },
107        ExecutionOutcome::Limit { limit, receipt } => {
108            DomainExecutionOutcome::Limit { limit, receipt }
109        }
110        ExecutionOutcome::Unsupported { feature, receipt } => {
111            DomainExecutionOutcome::Unsupported { feature, receipt }
112        }
113    }
114}
115
116/// Execute a byte-domain automaton over exact bytes.
117pub fn execute_bytes<E>(
118    automaton: &Automaton<u8, E>,
119    subject: &[u8],
120    limits: TextLimits,
121    extension_matches: impl Fn(&E, &u8) -> bool,
122) -> DomainExecutionOutcome<ByteDomain> {
123    typed(execute_regular(
124        automaton,
125        subject,
126        limits,
127        extension_matches,
128    ))
129}
130
131/// Execute a scalar-domain automaton over Unicode scalar values.
132pub fn execute_scalars<E>(
133    automaton: &Automaton<char, E>,
134    subject: &[char],
135    limits: TextLimits,
136    extension_matches: impl Fn(&E, &char) -> bool,
137) -> DomainExecutionOutcome<ScalarDomain> {
138    typed(execute_regular(
139        automaton,
140        subject,
141        limits,
142        extension_matches,
143    ))
144}
145
146/// Execute a code-unit-domain automaton over an exact `sim-text` value.
147///
148/// Unlike scalar execution, every position between adjacent `u16` values is
149/// addressable, including the middle of a surrogate pair and either side of a
150/// lone surrogate.
151///
152/// ```compile_fail
153/// use sim_lib_pattern::{CodeUnitOffset, ScalarOffset, require_code_unit_offset};
154/// require_code_unit_offset(ScalarOffset::new(1));
155/// ```
156pub fn execute_code_units<E>(
157    automaton: &Automaton<u16, E>,
158    subject: &CodeUnitString,
159    limits: TextLimits,
160    extension_matches: impl Fn(&E, &u16) -> bool,
161) -> DomainExecutionOutcome<CodeUnitDomain> {
162    typed(execute_regular(
163        automaton,
164        subject.as_code_units(),
165        limits,
166        extension_matches,
167    ))
168}
169
170/// Type-checking witness used by APIs that require an exact code-unit offset.
171pub const fn require_code_unit_offset(offset: CodeUnitOffset) -> CodeUnitOffset {
172    offset
173}