substrate_data/forms/text/
parser.rs1use super::{Doc, LineCol, Span, Spans, SyntaxError, scan};
4use crate::model::value::{Map, Value};
5
6const HEX_RADIX: u32 = 16;
8
9pub const MAX_DEPTH: usize = 256;
11
12pub fn parse(text: &str) -> Result<Doc, SyntaxError> {
18 let (value, spans) = run::<true>(text)?;
19 Ok(Doc { value, spans })
20}
21
22pub fn parse_value(text: &str) -> Result<Value, SyntaxError> {
28 run::<false>(text).map(|(v, _)| v)
29}
30
31fn run<const SPANS: bool>(text: &str) -> Result<(Value, Spans), SyntaxError> {
32 let mut p = Parser::<SPANS> {
33 src: text.as_bytes(),
34 text,
35 i: 0,
36 depth: 0,
37 };
38 if text.starts_with('\u{feff}') {
39 p.i = 3;
40 }
41 p.ws()?;
42 let (value, spans) = p.value()?;
43 p.ws()?;
44 if p.i < p.src.len() {
45 return Err(p.error("unexpected text after the value"));
46 }
47 Ok((value, spans))
48}
49
50#[derive(Debug)]
52struct Parser<'a, const SPANS: bool> {
53 src: &'a [u8],
54 text: &'a str,
55 i: usize,
56 depth: usize,
57}
58
59impl<const SPANS: bool> Parser<'_, SPANS> {
60 fn error(&self, message: impl Into<String>) -> SyntaxError {
61 self.error_at(self.i, message)
62 }
63
64 fn error_at(&self, offset: usize, message: impl Into<String>) -> SyntaxError {
65 let mut message = message.into();
66 if offset >= self.src.len() {
67 message.push_str(", found the end of the file");
68 }
69 SyntaxError {
70 message,
71 at: LineCol::of(self.text, offset),
72 offset,
73 }
74 }
75
76 fn peek(&self) -> Option<u8> {
77 self.src.get(self.i).copied()
78 }
79
80 fn ws(&mut self) -> Result<(), SyntaxError> {
82 loop {
83 let rest = &self.src[self.i..];
84 self.i += rest
85 .iter()
86 .position(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
87 .unwrap_or(rest.len());
88 if self.peek() != Some(b'/') {
89 return Ok(());
90 }
91 match self.src.get(self.i + 1) {
92 Some(b'/') => {
93 self.i += scan::line_end(&self.src[self.i..]);
94 }
95 Some(b'*') => {
96 let start = self.i;
97 match scan::block_comment_end(&self.src[self.i + 2..]) {
98 Some(end) => self.i += 2 + end + 2,
99 None => return Err(self.error_at(start, "unclosed `/*` comment")),
100 }
101 }
102 _ => return Ok(()),
103 }
104 }
105 }
106
107 fn value(&mut self) -> Result<(Value, Spans), SyntaxError> {
108 let start = self.i;
109 let (value, children) = match self.peek() {
110 Some(b'{') => self.nested(Parser::map)?,
111 Some(b'[') => self.nested(Parser::list)?,
112 Some(b'"') => (Value::Str(self.string()?), Vec::new()),
113 Some(b'-' | b'0'..=b'9') => (self.number()?, Vec::new()),
114 Some(b't') if self.src[self.i..].starts_with(b"true") => {
115 self.i += 4;
116 (Value::Bool(true), Vec::new())
117 }
118 Some(b'f') if self.src[self.i..].starts_with(b"false") => {
119 self.i += 5;
120 (Value::Bool(false), Vec::new())
121 }
122 Some(b'n') if self.src[self.i..].starts_with(b"null") => {
123 self.i += 4;
124 (Value::Null, Vec::new())
125 }
126 Some(b'\'') => return Err(self.error("strings use double quotes")),
127 _ => return Err(self.error("expected a value")),
128 };
129 let span = Span::new(start, self.i);
130 Ok((
131 value,
132 Spans {
133 span,
134 key: None,
135 children,
136 },
137 ))
138 }
139
140 fn nested(
141 &mut self,
142 f: impl FnOnce(&mut Self) -> Result<(Value, Vec<Spans>), SyntaxError>,
143 ) -> Result<(Value, Vec<Spans>), SyntaxError> {
144 self.depth += 1;
145 if self.depth > MAX_DEPTH {
146 return Err(self.error(format!("nested deeper than {MAX_DEPTH} levels")));
147 }
148 let r = f(self);
149 self.depth -= 1;
150 r
151 }
152
153 fn list(&mut self) -> Result<(Value, Vec<Spans>), SyntaxError> {
154 self.i += 1;
155 let mut items = Vec::new();
156 let mut spans = Vec::new();
157 loop {
158 self.ws()?;
159 if self.peek() == Some(b']') {
160 self.i += 1;
161 break;
162 }
163 let (v, s) = self.value()?;
164 items.push(v);
165 if SPANS {
166 spans.push(s);
167 }
168 self.ws()?;
169 match self.peek() {
170 Some(b',') => self.i += 1,
171 Some(b']') => {
172 self.i += 1;
173 break;
174 }
175 _ => return Err(self.error("expected `,` or `]`")),
176 }
177 }
178 Ok((Value::List(items), spans))
179 }
180
181 fn map(&mut self) -> Result<(Value, Vec<Spans>), SyntaxError> {
182 self.i += 1;
183 let mut map = Map::new();
184 let mut spans = Vec::new();
185 let mut seen: Option<std::collections::HashSet<String>> = None;
187 loop {
188 self.ws()?;
189 if self.peek() == Some(b'}') {
190 self.i += 1;
191 break;
192 }
193 let key_start = self.i;
194 let key = self.key()?;
195 let key_span = Span::new(key_start, self.i);
196 if is_duplicate(&map, &mut seen, &key) {
197 return Err(
198 self.error_at(key_start, format!("`{key}` is already set in this object"))
199 );
200 }
201 self.ws()?;
202 if self.peek() != Some(b':') {
203 return Err(self.error("expected `:` after the key"));
204 }
205 self.i += 1;
206 self.ws()?;
207 let (v, mut s) = self.value()?;
208 s.key = Some(key_span);
209 map.push_unchecked(key.into(), v);
210 if SPANS {
211 spans.push(s);
212 }
213 self.ws()?;
214 match self.peek() {
215 Some(b',') => self.i += 1,
216 Some(b'}') => {
217 self.i += 1;
218 break;
219 }
220 _ => return Err(self.error("expected `,` or `}`")),
221 }
222 }
223 Ok((Value::Map(map), spans))
224 }
225
226 fn key(&mut self) -> Result<String, SyntaxError> {
228 let start = self.i;
229 match self.peek() {
230 Some(b'"') => self.string(),
231 Some(first) if first.is_ascii_alphabetic() || first == b'_' || first == b'$' => {
232 while self
233 .peek()
234 .is_some_and(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'$')
235 {
236 self.i += 1;
237 }
238 Ok(self.text[start..self.i].to_string())
239 }
240 _ => Err(self.error("expected a key")),
241 }
242 }
243
244 fn string(&mut self) -> Result<String, SyntaxError> {
245 let open = self.i;
246 self.i += 1;
247 let mut out = String::new();
248 loop {
249 let Some(n) = scan::string_stop(&self.src[self.i..]) else {
250 return Err(self.error_at(open, "unclosed string"));
251 };
252 out.push_str(&self.text[self.i..self.i + n]);
253 self.i += n;
254 match self.src[self.i] {
255 b'"' => {
256 self.i += 1;
257 return Ok(out);
258 }
259 b'\\' => self.escape(&mut out)?,
260 b'\n' => return Err(self.error("line break in a string (write `\\n`)")),
261 _ => return Err(self.error("control character in a string")),
262 }
263 }
264 }
265
266 fn escape(&mut self, out: &mut String) -> Result<(), SyntaxError> {
267 let at = self.i;
268 self.i += 1;
269 let c = self.peek().ok_or_else(|| self.error("unclosed string"))?;
270 self.i += 1;
271 match c {
272 b'"' => out.push('"'),
273 b'\\' => out.push('\\'),
274 b'/' => out.push('/'),
275 b'b' => out.push('\u{8}'),
276 b'f' => out.push('\u{c}'),
277 b'n' => out.push('\n'),
278 b'r' => out.push('\r'),
279 b't' => out.push('\t'),
280 b'u' => {
281 let hi = self.hex4(at)?;
282 let ch = if (0xD800..0xDC00).contains(&hi) {
283 if !self.src[self.i..].starts_with(b"\\u") {
284 return Err(self.error_at(at, "unpaired surrogate in `\\u` escape"));
285 }
286 self.i += 2;
287 let lo = self.hex4(at)?;
288 if !(0xDC00..0xE000).contains(&lo) {
289 return Err(self.error_at(at, "unpaired surrogate in `\\u` escape"));
290 }
291 char::from_u32(0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00))
292 } else {
293 char::from_u32(hi)
294 };
295 out.push(
296 ch.ok_or_else(|| self.error_at(at, "unpaired surrogate in `\\u` escape"))?,
297 );
298 }
299 _ => {
300 return Err(self.error_at(
301 at,
302 "unknown escape (`\\n`, `\\t`, `\\\"`, `\\\\`, `\\uXXXX`, ...)",
303 ));
304 }
305 }
306 Ok(())
307 }
308
309 fn hex4(&mut self, at: usize) -> Result<u32, SyntaxError> {
310 let digits = self
311 .text
312 .get(self.i..self.i + 4)
313 .filter(|d| d.bytes().all(|b| b.is_ascii_hexdigit()));
314 let d = digits.ok_or_else(|| self.error_at(at, "`\\u` needs four hex digits"))?;
315 let code = u32::from_str_radix(d, HEX_RADIX)
316 .map_err(|_| self.error_at(at, "`\\u` needs four hex digits"))?;
317 self.i += 4;
318 Ok(code)
319 }
320
321 fn number(&mut self) -> Result<Value, SyntaxError> {
322 let start = self.i;
323 if self.peek() == Some(b'-') {
324 self.i += 1;
325 }
326 let digits = |p: &mut Self| {
327 let s = p.i;
328 while p.peek().is_some_and(|c| c.is_ascii_digit()) {
329 p.i += 1;
330 }
331 p.i - s
332 };
333 let int_start = self.i;
334 if digits(self) == 0 {
335 return Err(self.error("expected digits"));
336 }
337 if self.src[int_start] == b'0' && self.i - int_start > 1 {
338 return Err(self.error_at(int_start, "numbers don't start with 0"));
339 }
340 let mut float = false;
341 if self.peek() == Some(b'.') {
342 self.i += 1;
343 float = true;
344 if digits(self) == 0 {
345 return Err(self.error("expected digits after `.`"));
346 }
347 }
348 if matches!(self.peek(), Some(b'e' | b'E')) {
349 self.i += 1;
350 float = true;
351 if matches!(self.peek(), Some(b'+' | b'-')) {
352 self.i += 1;
353 }
354 if digits(self) == 0 {
355 return Err(self.error("expected digits in the exponent"));
356 }
357 }
358 let s = &self.text[start..self.i];
359 if !float && let Ok(n) = s.parse::<i64>() {
360 return Ok(Value::Int(n));
361 }
362 Ok(Value::Float(
363 s.parse::<f64>()
364 .map_err(|_| self.error_at(start, "invalid number"))?,
365 ))
366 }
367}
368
369fn is_duplicate(
372 map: &Map,
373 seen: &mut Option<std::collections::HashSet<String>>,
374 key: &str,
375) -> bool {
376 match seen {
377 Some(set) => !set.insert(key.to_string()),
378 None if map.len() < 16 => map.get(key).is_some(),
379 None => {
380 let mut set: std::collections::HashSet<String> =
381 map.keys().map(str::to_string).collect();
382 let dup = !set.insert(key.to_string());
383 *seen = Some(set);
384 dup
385 }
386 }
387}