marco_core/parser/
shared.rs1use crate::parser::position::{Position, Span as ParserSpan};
5use nom_locate::LocatedSpan;
6use std::cell::Cell;
7
8thread_local! {
13 static TRACK_POSITIONS: Cell<bool> = const { Cell::new(true) };
14 static PARSE_MATH: Cell<bool> = const { Cell::new(true) };
15 static PARSE_DIAGRAMS: Cell<bool> = const { Cell::new(true) };
16}
17
18pub(crate) struct ParseOptionsGuard {
23 prev_track: bool,
24 prev_math: bool,
25 prev_diagrams: bool,
26}
27
28impl ParseOptionsGuard {
29 pub(crate) fn new(track: bool, math: bool, diagrams: bool) -> Self {
30 let prev_track = TRACK_POSITIONS.with(|c| c.replace(track));
31 let prev_math = PARSE_MATH.with(|c| c.replace(math));
32 let prev_diagrams = PARSE_DIAGRAMS.with(|c| c.replace(diagrams));
33 Self {
34 prev_track,
35 prev_math,
36 prev_diagrams,
37 }
38 }
39}
40
41impl Drop for ParseOptionsGuard {
42 fn drop(&mut self) {
43 TRACK_POSITIONS.with(|c| c.set(self.prev_track));
44 PARSE_MATH.with(|c| c.set(self.prev_math));
45 PARSE_DIAGRAMS.with(|c| c.set(self.prev_diagrams));
46 }
47}
48
49#[cfg(feature = "parallel-parse")]
58#[inline]
59pub(crate) fn track_positions_enabled() -> bool {
60 TRACK_POSITIONS.with(|c| c.get())
61}
62
63#[inline]
65pub(crate) fn parse_math_enabled() -> bool {
66 PARSE_MATH.with(|c| c.get())
67}
68
69#[inline]
71pub(crate) fn parse_diagrams_enabled() -> bool {
72 PARSE_DIAGRAMS.with(|c| c.get())
73}
74
75#[inline]
84pub fn opt_span(span: GrammarSpan) -> Option<ParserSpan> {
85 if !TRACK_POSITIONS.with(|c| c.get()) {
86 return None;
87 }
88 Some(to_parser_span(span))
89}
90
91#[inline]
96pub fn opt_span_range(start: GrammarSpan, end: GrammarSpan) -> Option<ParserSpan> {
97 if !TRACK_POSITIONS.with(|c| c.get()) {
98 return None;
99 }
100 Some(to_parser_span_range(start, end))
101}
102
103#[inline]
108pub fn opt_span_range_inclusive(start: GrammarSpan, end: GrammarSpan) -> Option<ParserSpan> {
109 if !TRACK_POSITIONS.with(|c| c.get()) {
110 return None;
111 }
112 Some(to_parser_span_range_inclusive(start, end))
113}
114
115pub type GrammarSpan<'a> = LocatedSpan<&'a str>;
117
118pub fn to_parser_span(span: GrammarSpan) -> ParserSpan {
124 let start_line = span.location_line() as usize; let frag = span.fragment().as_bytes();
126
127 let mut newline_count = 0usize;
129 let mut last_nl: Option<usize> = None;
130 for (i, &b) in frag.iter().enumerate() {
131 if b == b'\n' {
132 newline_count += 1;
133 last_nl = Some(i);
134 }
135 }
136 let end_line = start_line + newline_count;
137
138 let end_column = match last_nl {
139 Some(pos) if pos == frag.len() - 1 => {
140 1
142 }
143 Some(pos) => {
144 frag.len() - pos - 1 + 1
146 }
147 None => {
148 span.get_column() + frag.len()
150 }
151 };
152
153 let start = Position::new(start_line, span.get_column(), span.location_offset());
154 let end = Position::new(
155 end_line,
156 end_column,
157 span.location_offset() + span.fragment().len(),
158 );
159 ParserSpan::new(start, end)
160}
161
162pub fn to_parser_span_range(start: GrammarSpan, end: GrammarSpan) -> ParserSpan {
168 let start_pos = Position::new(
169 start.location_line() as usize,
170 start.get_column(),
171 start.location_offset(),
172 );
173 let end_pos = Position::new(
174 end.location_line() as usize,
175 end.get_column(),
176 end.location_offset(),
177 );
178 ParserSpan::new(start_pos, end_pos)
179}
180
181pub fn to_parser_span_range_inclusive(start: GrammarSpan, end: GrammarSpan) -> ParserSpan {
186 let start_pos = Position::new(
187 start.location_line() as usize,
188 start.get_column(),
189 start.location_offset(),
190 );
191 let end_pos = Position::new(
192 end.location_line() as usize,
193 end.get_column() + end.fragment().len(),
194 end.location_offset() + end.fragment().len(),
195 );
196 ParserSpan::new(start_pos, end_pos)
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 #[test]
204 fn test_to_parser_span_ascii() {
205 let input = GrammarSpan::new("hello");
206 let span = to_parser_span(input);
207 assert_eq!(span.start.line, 1);
208 assert_eq!(span.start.column, 1);
209 assert_eq!(span.end.column, 6); }
211
212 #[test]
213 fn test_to_parser_span_utf8_and_emoji() {
214 let input = GrammarSpan::new("Tëst");
215 let span = to_parser_span(input);
216 assert_eq!(span.start.column, 1);
217 assert_eq!(span.end.column, 6);
219
220 let input2 = GrammarSpan::new("🎨");
221 let span2 = to_parser_span(input2);
222 assert_eq!(span2.start.column, 1);
223 assert_eq!(span2.end.column, 5);
225 }
226}