regex_anre/context.rs
1// Copyright (c) 2026 Hemashushu <hippospark@gmail.com>, All rights reserved.
2//
3// This Source Code Form is subject to the terms of
4// the Mozilla Public License version 2.0 and additional exceptions.
5// For more details, see the LICENSE, LICENSE.additional, and CONTRIBUTING files.
6
7use crate::object::Map;
8
9/// Context for matching process.
10pub struct Context<'a> {
11 // The UTF-8 byte stream of the source text.
12 pub bytes: &'a [u8],
13
14 // Routines.
15 //
16 // A routine corresponds to a route.
17 // A process has multiple routines since a `Map` can contain multiple routes,
18 // where the first routine is the main routine.
19 //
20 // Routines are similar to threads in programming languages.
21 // But at any given time in processor, only one routine is active, so
22 // the processor is single-threaded.
23 pub routines: Vec<Routine>,
24
25 // Pre-allocated capture group slots.
26 // These are the executing results of the current process.
27 pub matched_slots: Vec<MatchRange>,
28
29 // Pre-allocated counter slots.
30 pub counter_slots: Vec<usize>,
31}
32
33/// Represents a routine generated by a route during process execution.
34///
35/// Routines are similar to threads in programming languages, but at any given time in ANRE engine,
36/// only one routine is executing. When a process enters another routine, the current routine is blocked
37///
38/// a process may have multiple routines since a `Map` can contain multiple routes.
39pub struct Routine {
40 // The specified start position (inclusive, in bytes) of the source text.
41 //
42 // The main routine starts at position 0, and end position is the length of the source text,
43 // but sub-routines (which are generated by look-around assertions) can have different start and end positions.
44 pub range_start: usize,
45
46 // The specified end position (exclusive, in bytes) of the source text.
47 pub range_end: usize,
48
49 // Index of the corresponding route.
50 pub route_index: usize,
51
52 // Transitions are similar to functions in programming languages.
53 // `transition_stack` is similar to a call stack.
54 //
55 // In detail, a route consists of multiple nodes, when a routine is running,
56 // it traverses nodes one by one based on transitions. Each node contains one or more transitions,
57 // these transitions are pushed onto this stack for backtracking purposes.
58 //
59 // When a transition is executed, it is popped from the stack:
60 // - On failure: The next transition is popped and tried. If the stack is empty,
61 // it indicates that the route matching has failed.
62 // - On success: The routine moves to the next node pointed to by the transition
63 // and pushes all transitions of the new node onto the stack. If the node is the
64 // last node (which is called the exit node) of the route, it means the route matching has succeeded.
65 pub transition_stack: Vec<TransitionStackItem>,
66}
67
68impl Routine {
69 pub fn new(range_start: usize, range_end: usize, route_index: usize) -> Routine {
70 Routine {
71 range_start,
72 range_end,
73 route_index,
74 transition_stack: vec![],
75 }
76 }
77}
78
79/// Represents an item in the transition stack of a routine.
80///
81/// The transition stack is similar to a call stack in programming languages.
82/// Note that the `cursor` and `repetition_count` should be stored in the stack item
83/// instead of the context because they are specific to the transition being executed and may change during backtracking.
84pub struct TransitionStackItem {
85 // Current position (in bytes) of the source text.
86 // This position is a cursor that is updated when a transition is executed.
87 pub cursor: usize,
88
89 // Current repetition count for the repetition transition.
90 pub repetition_count: usize,
91
92 // Index of the node holding the transitions.
93 pub current_node_index: usize,
94
95 // Index of the current transition.
96 pub transition_index: usize,
97}
98
99impl TransitionStackItem {
100 pub fn new(
101 cursor: usize,
102 repetition_count: usize,
103 current_node_index: usize,
104 transition_index: usize,
105 ) -> Self {
106 TransitionStackItem {
107 cursor,
108 repetition_count,
109 current_node_index,
110 transition_index,
111 }
112 }
113}
114
115/// Represents the text range of capture group.
116#[derive(Debug, PartialEq, Clone, Default)]
117pub struct MatchRange {
118 // Start position (inclusive) of the source text
119 // (in bytes) for the capture group.
120 pub start: usize,
121
122 // End position (exclusive) of the source text
123 // (in bytes) for the capture group.
124 pub end: usize,
125}
126
127impl MatchRange {
128 pub fn new(start: usize, end: usize) -> Self {
129 MatchRange { start, end }
130 }
131}
132
133impl<'a> Context<'a> {
134 pub fn new(
135 s: &'a str,
136 number_of_capture_groups: usize, // Number of pre-allocated match slots.
137 number_of_counters: usize, // Number of pre-allocated counter slots.
138 ) -> Self {
139 let bytes = s.as_bytes();
140 Self::from_bytes(bytes, number_of_capture_groups, number_of_counters)
141 }
142
143 pub fn from_bytes(
144 bytes: &'a [u8],
145 number_of_capture_groups: usize, // Number of pre-allocated match slots.
146 number_of_counters: usize, // Number of pre-allocated counter slots.
147 ) -> Self {
148 Context {
149 bytes,
150 routines: vec![],
151
152 // Allocate the vector of `usize` for the counter slots.
153 counter_slots: vec![0; number_of_counters],
154
155 // Allocate the vector of `MatchRange` for the capture group slots.
156 matched_slots: vec![MatchRange::default(); number_of_capture_groups],
157 }
158 }
159
160 pub fn push_transitions_of_node(
161 &mut self,
162 map: &Map,
163 node_index: usize,
164 cursor: usize,
165 repetition_count: usize,
166 ) {
167 let routine = self.get_current_routine_ref_mut();
168 let route_index = routine.route_index;
169 let node = &map.routes[route_index].nodes[node_index];
170 let transition_count = node.path.len();
171
172 for transition_index in (0..transition_count).rev() {
173 routine.transition_stack.push(TransitionStackItem::new(
174 cursor,
175 repetition_count,
176 node_index,
177 transition_index,
178 ));
179 }
180 }
181
182 pub fn pop_transition_stack_item(&mut self) -> Option<TransitionStackItem> {
183 self.get_current_routine_ref_mut().transition_stack.pop()
184 }
185
186 #[inline]
187 pub fn get_current_routine_ref(&self) -> &Routine {
188 self.routines.last().unwrap()
189 }
190
191 #[inline]
192 pub fn get_current_routine_ref_mut(&mut self) -> &mut Routine {
193 let count = self.routines.len();
194 &mut self.routines[count - 1]
195 }
196}