1use std::fmt;
9
10use crate::value::{Map, Path, Seg, Value};
11
12pub const MAX_DEPTH: usize = 256;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
17pub struct Span {
18 pub start: u32,
19 pub end: u32,
20}
21
22#[derive(Debug, Clone, Default, PartialEq)]
25pub struct Spans {
26 pub span: Span,
27 pub key: Option<Span>,
28 pub children: Vec<Spans>,
29}
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct Doc {
34 pub value: Value,
35 pub spans: Spans,
36}
37
38impl Doc {
39 pub fn spans_at(&self, path: &Path) -> &Spans {
42 let mut v = &self.value;
43 let mut s = &self.spans;
44 for seg in path.segments() {
45 let i = match (seg, v) {
46 (Seg::Key(k), Value::Map(m)) => m.index_of(k),
47 (Seg::Index(i), Value::List(l)) if *i < l.len() => Some(*i),
48 _ => None,
49 };
50 let Some(i) = i else { break };
51 let Some(child) = s.children.get(i) else {
52 break;
53 };
54 v = match v {
55 Value::Map(m) => m.value_at(i).unwrap_or(v),
56 Value::List(l) => &l[i],
57 _ => v,
58 };
59 s = child;
60 }
61 s
62 }
63
64 pub fn locate(&self, text: &str, path: &Path) -> LineCol {
66 LineCol::of(text, self.spans_at(path).span.start as usize)
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct LineCol {
73 pub line: u32,
74 pub col: u32,
75}
76
77impl LineCol {
78 pub fn of(text: &str, offset: usize) -> LineCol {
80 let offset = offset.min(text.len());
81 let before = &text.as_bytes()[..offset];
82 let line = before.iter().filter(|&&b| b == b'\n').count() as u32 + 1;
83 let line_start = before
84 .iter()
85 .rposition(|&b| b == b'\n')
86 .map_or(0, |i| i + 1);
87 let col = text
88 .get(line_start..offset)
89 .map_or(offset - line_start, |s| s.chars().count()) as u32
90 + 1;
91 LineCol { line, col }
92 }
93}
94
95impl fmt::Display for LineCol {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(f, "{}:{}", self.line, self.col)
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct SyntaxError {
104 pub message: String,
105 pub at: LineCol,
106 pub offset: usize,
107}
108
109impl fmt::Display for SyntaxError {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 write!(f, "{}: {}", self.at, self.message)
112 }
113}
114
115impl std::error::Error for SyntaxError {}
116
117pub fn parse(text: &str) -> Result<Doc, SyntaxError> {
119 let (value, spans) = run::<true>(text)?;
120 Ok(Doc { value, spans })
121}
122
123pub fn parse_value(text: &str) -> Result<Value, SyntaxError> {
125 run::<false>(text).map(|(v, _)| v)
126}
127
128fn run<const SPANS: bool>(text: &str) -> Result<(Value, Spans), SyntaxError> {
129 let mut p = Parser::<SPANS> {
130 src: text.as_bytes(),
131 text,
132 i: 0,
133 depth: 0,
134 };
135 if text.starts_with('\u{feff}') {
136 p.i = 3;
137 }
138 p.ws()?;
139 let (value, spans) = p.value()?;
140 p.ws()?;
141 if p.i < p.src.len() {
142 return Err(p.error("unexpected text after the value"));
143 }
144 Ok((value, spans))
145}
146
147struct Parser<'a, const SPANS: bool> {
149 src: &'a [u8],
150 text: &'a str,
151 i: usize,
152 depth: usize,
153}
154
155impl<const SPANS: bool> Parser<'_, SPANS> {
156 fn error(&self, message: impl Into<String>) -> SyntaxError {
157 self.error_at(self.i, message)
158 }
159
160 fn error_at(&self, offset: usize, message: impl Into<String>) -> SyntaxError {
161 let mut message = message.into();
162 if offset >= self.src.len() {
163 message.push_str(", found the end of the file");
164 }
165 SyntaxError {
166 message,
167 at: LineCol::of(self.text, offset),
168 offset,
169 }
170 }
171
172 fn peek(&self) -> Option<u8> {
173 self.src.get(self.i).copied()
174 }
175
176 fn ws(&mut self) -> Result<(), SyntaxError> {
178 loop {
179 let rest = &self.src[self.i..];
180 self.i += rest
181 .iter()
182 .position(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
183 .unwrap_or(rest.len());
184 if self.peek() != Some(b'/') {
185 return Ok(());
186 }
187 match self.src.get(self.i + 1) {
188 Some(b'/') => {
189 let rest = &self.src[self.i..];
190 self.i += rest.iter().position(|&b| b == b'\n').unwrap_or(rest.len());
191 }
192 Some(b'*') => {
193 let start = self.i;
194 match self.text[self.i + 2..].find("*/") {
195 Some(end) => self.i += 2 + end + 2,
196 None => return Err(self.error_at(start, "unclosed `/*` comment")),
197 }
198 }
199 _ => return Ok(()),
200 }
201 }
202 }
203
204 fn value(&mut self) -> Result<(Value, Spans), SyntaxError> {
205 let start = self.i;
206 let (value, children) = match self.peek() {
207 Some(b'{') => self.nested(|p| p.map())?,
208 Some(b'[') => self.nested(|p| p.list())?,
209 Some(b'"') => (Value::Str(self.string()?), Vec::new()),
210 Some(b'-' | b'0'..=b'9') => (self.number()?, Vec::new()),
211 Some(b't') if self.src[self.i..].starts_with(b"true") => {
212 self.i += 4;
213 (Value::Bool(true), Vec::new())
214 }
215 Some(b'f') if self.src[self.i..].starts_with(b"false") => {
216 self.i += 5;
217 (Value::Bool(false), Vec::new())
218 }
219 Some(b'n') if self.src[self.i..].starts_with(b"null") => {
220 self.i += 4;
221 (Value::Null, Vec::new())
222 }
223 Some(b'\'') => return Err(self.error("strings use double quotes")),
224 _ => return Err(self.error("expected a value")),
225 };
226 let span = Span {
227 start: start as u32,
228 end: self.i as u32,
229 };
230 Ok((
231 value,
232 Spans {
233 span,
234 key: None,
235 children,
236 },
237 ))
238 }
239
240 fn nested(
241 &mut self,
242 f: impl FnOnce(&mut Self) -> Result<(Value, Vec<Spans>), SyntaxError>,
243 ) -> Result<(Value, Vec<Spans>), SyntaxError> {
244 self.depth += 1;
245 if self.depth > MAX_DEPTH {
246 return Err(self.error(format!("nested deeper than {MAX_DEPTH} levels")));
247 }
248 let r = f(self);
249 self.depth -= 1;
250 r
251 }
252
253 fn list(&mut self) -> Result<(Value, Vec<Spans>), SyntaxError> {
254 self.i += 1;
255 let mut items = Vec::new();
256 let mut spans = Vec::new();
257 loop {
258 self.ws()?;
259 if self.peek() == Some(b']') {
260 self.i += 1;
261 break;
262 }
263 let (v, s) = self.value()?;
264 items.push(v);
265 if SPANS {
266 spans.push(s);
267 }
268 self.ws()?;
269 match self.peek() {
270 Some(b',') => self.i += 1,
271 Some(b']') => {
272 self.i += 1;
273 break;
274 }
275 _ => return Err(self.error("expected `,` or `]`")),
276 }
277 }
278 Ok((Value::List(items), spans))
279 }
280
281 fn map(&mut self) -> Result<(Value, Vec<Spans>), SyntaxError> {
282 self.i += 1;
283 let mut map = Map::new();
284 let mut spans = Vec::new();
285 let mut seen: Option<std::collections::HashSet<String>> = None;
287 loop {
288 self.ws()?;
289 if self.peek() == Some(b'}') {
290 self.i += 1;
291 break;
292 }
293 let key_start = self.i;
294 let key = match self.peek() {
295 Some(b'"') => self.string()?,
296 Some(c) if c.is_ascii_alphabetic() || c == b'_' || c == b'$' => {
297 while self
298 .peek()
299 .is_some_and(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'$')
300 {
301 self.i += 1;
302 }
303 self.text[key_start..self.i].to_string()
304 }
305 _ => return Err(self.error("expected a key")),
306 };
307 let key_span = Span {
308 start: key_start as u32,
309 end: self.i as u32,
310 };
311 let duplicate = match &mut seen {
312 Some(set) => !set.insert(key.clone()),
313 None if map.len() < 16 => map.get(&key).is_some(),
314 None => {
315 let mut set: std::collections::HashSet<String> =
316 map.keys().map(str::to_string).collect();
317 let dup = !set.insert(key.clone());
318 seen = Some(set);
319 dup
320 }
321 };
322 if duplicate {
323 return Err(
324 self.error_at(key_start, format!("`{key}` is already set in this object"))
325 );
326 }
327 self.ws()?;
328 if self.peek() != Some(b':') {
329 return Err(self.error("expected `:` after the key"));
330 }
331 self.i += 1;
332 self.ws()?;
333 let (v, mut s) = self.value()?;
334 s.key = Some(key_span);
335 map.push_unchecked(key.into(), v);
336 if SPANS {
337 spans.push(s);
338 }
339 self.ws()?;
340 match self.peek() {
341 Some(b',') => self.i += 1,
342 Some(b'}') => {
343 self.i += 1;
344 break;
345 }
346 _ => return Err(self.error("expected `,` or `}`")),
347 }
348 }
349 Ok((Value::Map(map), spans))
350 }
351
352 fn string(&mut self) -> Result<String, SyntaxError> {
353 let open = self.i;
354 self.i += 1;
355 let mut out = String::new();
356 loop {
357 let rest = &self.src[self.i..];
358 let Some(n) = rest
359 .iter()
360 .position(|&b| b == b'"' || b == b'\\' || b < 0x20)
361 else {
362 return Err(self.error_at(open, "unclosed string"));
363 };
364 out.push_str(&self.text[self.i..self.i + n]);
365 self.i += n;
366 match self.src[self.i] {
367 b'"' => {
368 self.i += 1;
369 return Ok(out);
370 }
371 b'\\' => self.escape(&mut out)?,
372 b'\n' => return Err(self.error("line break in a string (write `\\n`)")),
373 _ => return Err(self.error("control character in a string")),
374 }
375 }
376 }
377
378 fn escape(&mut self, out: &mut String) -> Result<(), SyntaxError> {
379 let at = self.i;
380 self.i += 1;
381 let c = self.peek().ok_or_else(|| self.error("unclosed string"))?;
382 self.i += 1;
383 match c {
384 b'"' => out.push('"'),
385 b'\\' => out.push('\\'),
386 b'/' => out.push('/'),
387 b'b' => out.push('\u{8}'),
388 b'f' => out.push('\u{c}'),
389 b'n' => out.push('\n'),
390 b'r' => out.push('\r'),
391 b't' => out.push('\t'),
392 b'u' => {
393 let hi = self.hex4(at)?;
394 let ch = if (0xD800..0xDC00).contains(&hi) {
395 if !self.src[self.i..].starts_with(b"\\u") {
396 return Err(self.error_at(at, "unpaired surrogate in `\\u` escape"));
397 }
398 self.i += 2;
399 let lo = self.hex4(at)?;
400 if !(0xDC00..0xE000).contains(&lo) {
401 return Err(self.error_at(at, "unpaired surrogate in `\\u` escape"));
402 }
403 char::from_u32(0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00))
404 } else {
405 char::from_u32(hi)
406 };
407 out.push(
408 ch.ok_or_else(|| self.error_at(at, "unpaired surrogate in `\\u` escape"))?,
409 );
410 }
411 _ => {
412 return Err(self.error_at(
413 at,
414 "unknown escape (`\\n`, `\\t`, `\\\"`, `\\\\`, `\\uXXXX`, ...)",
415 ));
416 }
417 }
418 Ok(())
419 }
420
421 fn hex4(&mut self, at: usize) -> Result<u32, SyntaxError> {
422 let digits = self
423 .text
424 .get(self.i..self.i + 4)
425 .filter(|d| d.bytes().all(|b| b.is_ascii_hexdigit()));
426 let d = digits.ok_or_else(|| self.error_at(at, "`\\u` needs four hex digits"))?;
427 self.i += 4;
428 Ok(u32::from_str_radix(d, 16).unwrap())
429 }
430
431 fn number(&mut self) -> Result<Value, SyntaxError> {
432 let start = self.i;
433 if self.peek() == Some(b'-') {
434 self.i += 1;
435 }
436 let digits = |p: &mut Self| {
437 let s = p.i;
438 while p.peek().is_some_and(|c| c.is_ascii_digit()) {
439 p.i += 1;
440 }
441 p.i - s
442 };
443 let int_start = self.i;
444 if digits(self) == 0 {
445 return Err(self.error("expected digits"));
446 }
447 if self.src[int_start] == b'0' && self.i - int_start > 1 {
448 return Err(self.error_at(int_start, "numbers don't start with 0"));
449 }
450 let mut float = false;
451 if self.peek() == Some(b'.') {
452 self.i += 1;
453 float = true;
454 if digits(self) == 0 {
455 return Err(self.error("expected digits after `.`"));
456 }
457 }
458 if matches!(self.peek(), Some(b'e' | b'E')) {
459 self.i += 1;
460 float = true;
461 if matches!(self.peek(), Some(b'+' | b'-')) {
462 self.i += 1;
463 }
464 if digits(self) == 0 {
465 return Err(self.error("expected digits in the exponent"));
466 }
467 }
468 let s = &self.text[start..self.i];
469 if !float && let Ok(n) = s.parse::<i64>() {
470 return Ok(Value::Int(n));
471 }
472 Ok(Value::Float(
473 s.parse::<f64>()
474 .map_err(|_| self.error_at(start, "invalid number"))?,
475 ))
476 }
477}
478
479pub fn to_string(value: &Value) -> String {
483 let mut out = String::with_capacity(256);
484 write(value, None, 0, &mut out);
485 out
486}
487
488pub fn to_string_pretty(value: &Value) -> String {
490 let mut out = String::new();
491 write(value, Some(" "), 0, &mut out);
492 out.push('\n');
493 out
494}
495
496fn write(value: &Value, indent: Option<&str>, level: usize, out: &mut String) {
497 let newline = |out: &mut String, level: usize| {
498 if let Some(unit) = indent {
499 out.push('\n');
500 for _ in 0..level {
501 out.push_str(unit);
502 }
503 }
504 };
505 match value {
506 Value::Null => out.push_str("null"),
507 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
508 Value::Int(n) => {
509 use std::fmt::Write as _;
510 let _ = write!(out, "{n}");
511 }
512 Value::Float(f) => write_float(*f, out),
513 Value::List(items) => {
514 if items.is_empty() {
515 out.push_str("[]");
516 return;
517 }
518 out.push('[');
519 for (i, v) in items.iter().enumerate() {
520 if i > 0 {
521 out.push(',');
522 }
523 newline(out, level + 1);
524 write(v, indent, level + 1, out);
525 }
526 newline(out, level);
527 out.push(']');
528 }
529 Value::Map(map) => {
530 if map.is_empty() {
531 out.push_str("{}");
532 return;
533 }
534 out.push('{');
535 for (i, (k, v)) in map.iter().enumerate() {
536 if i > 0 {
537 out.push(',');
538 }
539 newline(out, level + 1);
540 write_string(k, out);
541 out.push_str(if indent.is_some() { ": " } else { ":" });
542 write(v, indent, level + 1, out);
543 }
544 newline(out, level);
545 out.push('}');
546 }
547 Value::Str(s) | Value::Text(s) => {
548 if s.starts_with('@') {
549 out.push('"');
550 out.push('@');
551 write_string_body(s, out);
552 out.push('"');
553 } else {
554 write_string(s, out);
555 }
556 }
557 typed => write_string(&crate::typed::typed_string(typed).unwrap_or_default(), out),
558 }
559}
560
561fn write_float(f: f64, out: &mut String) {
562 if !f.is_finite() {
563 out.push_str("null");
564 } else if f.fract() == 0.0 && f.abs() < 1e16 {
565 out.push_str(&format!("{f:.1}"));
566 } else {
567 out.push_str(&f.to_string());
568 }
569}
570
571pub fn write_string(s: &str, out: &mut String) {
573 out.push('"');
574 write_string_body(s, out);
575 out.push('"');
576}
577
578fn write_string_body(s: &str, out: &mut String) {
579 let bytes = s.as_bytes();
580 let mut run = 0;
581 while let Some(n) = bytes[run..]
582 .iter()
583 .position(|&b| b == b'"' || b == b'\\' || b < 0x20)
584 {
585 let i = run + n;
586 out.push_str(&s[run..i]);
587 match bytes[i] {
588 b'"' => out.push_str("\\\""),
589 b'\\' => out.push_str("\\\\"),
590 b'\n' => out.push_str("\\n"),
591 b'\r' => out.push_str("\\r"),
592 b'\t' => out.push_str("\\t"),
593 b => {
594 use std::fmt::Write as _;
595 let _ = write!(out, "\\u{b:04x}");
596 }
597 }
598 run = i + 1;
599 }
600 out.push_str(&s[run..]);
601}
602
603#[cfg(test)]
604mod tests {
605 use super::*;
606
607 #[test]
608 fn json5_subset() {
609 let doc = parse("\u{feff}{\n // comment\n name: \"Ruby\", /* inline */ $schema: 1,\n \"list\": [1, 2.5, -3e2, true, null,],\n}").unwrap();
610 let m = doc.value.as_map().unwrap();
611 assert_eq!(m.keys().collect::<Vec<_>>(), ["name", "$schema", "list"]);
612 assert_eq!(
613 doc.value.get("list").unwrap(),
614 &Value::List(vec![
615 Value::Int(1),
616 Value::Float(2.5),
617 Value::Float(-300.0),
618 Value::Bool(true),
619 Value::Null
620 ])
621 );
622 }
623
624 #[test]
625 fn locates_values_and_keys() {
626 let text = "{\n \"a\": [\n 1,\n {\"b\": \"x\"}\n ]\n}";
627 let doc = parse(text).unwrap();
628 let path = Path::root().key("a").index(1).key("b");
629 assert_eq!(doc.locate(text, &path), LineCol { line: 4, col: 11 });
630 assert_eq!(
631 LineCol::of(text, doc.spans_at(&path).key.unwrap().start as usize),
632 LineCol { line: 4, col: 6 }
633 );
634 assert_eq!(
636 doc.locate(text, &Path::root().key("a").index(9)),
637 LineCol { line: 2, col: 8 }
638 );
639 }
640
641 #[test]
642 fn errors_say_where() {
643 let cases = [
644 ("{\"a\": 1,, }", "1:9", "expected a key"),
645 ("{\"a\": 1 \"b\": 2}", "1:9", "expected `,` or `}`"),
646 ("{\"a\": 1, \"a\": 2}", "1:10", "already set"),
647 ("[1, 2", "1:6", "found the end of the file"),
648 ("\"ab\ncd\"", "1:4", "line break"),
649 ("'x'", "1:1", "double quotes"),
650 ("01", "1:1", "start with 0"),
651 ("{} x", "1:4", "unexpected text"),
652 ("/* never closed", "1:1", "unclosed"),
653 ("\"\\q\"", "1:2", "unknown escape"),
654 ("\"\\ud800\"", "1:2", "surrogate"),
655 ("{\n \"é\": tru }", "2:8", "expected a value"),
656 ];
657 for (text, at, msg) in cases {
658 let e = parse(text).unwrap_err();
659 assert_eq!(e.at.to_string(), at, "{text:?}: {e}");
660 assert!(e.message.contains(msg), "{text:?}: {e}");
661 }
662 assert!(
663 parse(&"[".repeat(MAX_DEPTH + 1))
664 .unwrap_err()
665 .message
666 .contains("nested deeper")
667 );
668 }
669
670 #[test]
671 fn strings_and_numbers() {
672 let v = parse_value(
673 r#"["a\"b\\c\n\u00e9\ud83d\ude00", 9223372036854775807, 9223372036854775808, 0.1]"#,
674 )
675 .unwrap();
676 let l = v.as_list().unwrap();
677 assert_eq!(l[0], Value::Str("a\"b\\c\né😀".into()));
678 assert_eq!(l[1], Value::Int(i64::MAX));
679 assert_eq!(l[2], Value::Float(9223372036854775808.0));
680 assert_eq!(l[3], Value::Float(0.1));
681 }
682
683 #[test]
684 fn writes_what_it_reads() {
685 let text = "{\n \"name\": \"Ruby \\\"red\\\"\\n\",\n \"n\": [1, 2.0, 0.1, -5],\n \"empty\": {},\n \"none\": [],\n \"tab\\u0001\": null\n}\n";
686 let v = parse_value(text).unwrap();
687 assert_eq!(parse_value(&to_string(&v)).unwrap(), v);
688 let pretty = to_string_pretty(&v);
689 assert_eq!(parse_value(&pretty).unwrap(), v);
690 assert!(
691 pretty.starts_with(
692 "{\n \"name\": \"Ruby \\\"red\\\"\\n\",\n \"n\": [\n 1,\n 2.0,"
693 ),
694 "{pretty}"
695 );
696 assert_eq!(
697 to_string(&v),
698 r#"{"name":"Ruby \"red\"\n","n":[1,2.0,0.1,-5],"empty":{},"none":[],"tab\u0001":null}"#
699 );
700 }
701
702 #[test]
703 fn typed_values_are_strings_in_text() {
704 let v = Value::List(vec![
705 Value::Ref(crate::Ref::new("item", "minecraft:stick")),
706 Value::Color(0xFF112233),
707 Value::Str("@not a ref".into()),
708 ]);
709 assert_eq!(
710 to_string(&v),
711 r##"["@item:minecraft:stick","#112233","@@not a ref"]"##
712 );
713 }
714}