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#[inline]
51pub(crate) fn parse_math_enabled() -> bool {
52 PARSE_MATH.with(|c| c.get())
53}
54
55#[inline]
57pub(crate) fn parse_diagrams_enabled() -> bool {
58 PARSE_DIAGRAMS.with(|c| c.get())
59}
60
61#[inline]
70pub fn opt_span(span: GrammarSpan) -> Option<ParserSpan> {
71 if !TRACK_POSITIONS.with(|c| c.get()) {
72 return None;
73 }
74 Some(to_parser_span(span))
75}
76
77#[inline]
82pub fn opt_span_range(start: GrammarSpan, end: GrammarSpan) -> Option<ParserSpan> {
83 if !TRACK_POSITIONS.with(|c| c.get()) {
84 return None;
85 }
86 Some(to_parser_span_range(start, end))
87}
88
89#[inline]
94pub fn opt_span_range_inclusive(start: GrammarSpan, end: GrammarSpan) -> Option<ParserSpan> {
95 if !TRACK_POSITIONS.with(|c| c.get()) {
96 return None;
97 }
98 Some(to_parser_span_range_inclusive(start, end))
99}
100
101pub type GrammarSpan<'a> = LocatedSpan<&'a str>;
103
104pub fn to_parser_span(span: GrammarSpan) -> ParserSpan {
110 let start_line = span.location_line() as usize; let frag = span.fragment().as_bytes();
112
113 let mut newline_count = 0usize;
115 let mut last_nl: Option<usize> = None;
116 for (i, &b) in frag.iter().enumerate() {
117 if b == b'\n' {
118 newline_count += 1;
119 last_nl = Some(i);
120 }
121 }
122 let end_line = start_line + newline_count;
123
124 let end_column = match last_nl {
125 Some(pos) if pos == frag.len() - 1 => {
126 1
128 }
129 Some(pos) => {
130 frag.len() - pos - 1 + 1
132 }
133 None => {
134 span.get_column() + frag.len()
136 }
137 };
138
139 let start = Position::new(start_line, span.get_column(), span.location_offset());
140 let end = Position::new(
141 end_line,
142 end_column,
143 span.location_offset() + span.fragment().len(),
144 );
145 ParserSpan::new(start, end)
146}
147
148pub fn to_parser_span_range(start: GrammarSpan, end: GrammarSpan) -> ParserSpan {
154 let start_pos = Position::new(
155 start.location_line() as usize,
156 start.get_column(),
157 start.location_offset(),
158 );
159 let end_pos = Position::new(
160 end.location_line() as usize,
161 end.get_column(),
162 end.location_offset(),
163 );
164 ParserSpan::new(start_pos, end_pos)
165}
166
167pub fn to_parser_span_range_inclusive(start: GrammarSpan, end: GrammarSpan) -> ParserSpan {
172 let start_pos = Position::new(
173 start.location_line() as usize,
174 start.get_column(),
175 start.location_offset(),
176 );
177 let end_pos = Position::new(
178 end.location_line() as usize,
179 end.get_column() + end.fragment().len(),
180 end.location_offset() + end.fragment().len(),
181 );
182 ParserSpan::new(start_pos, end_pos)
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn test_to_parser_span_ascii() {
191 let input = GrammarSpan::new("hello");
192 let span = to_parser_span(input);
193 assert_eq!(span.start.line, 1);
194 assert_eq!(span.start.column, 1);
195 assert_eq!(span.end.column, 6); }
197
198 #[test]
199 fn test_to_parser_span_utf8_and_emoji() {
200 let input = GrammarSpan::new("Tëst");
201 let span = to_parser_span(input);
202 assert_eq!(span.start.column, 1);
203 assert_eq!(span.end.column, 6);
205
206 let input2 = GrammarSpan::new("🎨");
207 let span2 = to_parser_span(input2);
208 assert_eq!(span2.start.column, 1);
209 assert_eq!(span2.end.column, 5);
211 }
212}