1use std::fmt::Write;
10
11use num_bigint::BigInt;
12
13use crate::error::{DogeError, DogeResult};
14use crate::ordered_map::OrderedMap;
15use crate::stdlib::serialize::{escape_str, too_deep, unsupported, MAX_DEPTH};
16use crate::stdlib::str_arg;
17use crate::value::Value;
18
19const OCTAL_FRACTION_LIMIT: usize = 358;
24
25pub fn dson_parse(text: &Value) -> DogeResult {
28 let text = str_arg("dson", "parse", text)?;
29 let chars: Vec<char> = text.chars().collect();
30 let (toks, offs) = lex(&chars)?;
31 let mut p = Parser {
32 toks,
33 offs,
34 end: chars.len(),
35 pos: 0,
36 };
37 let value = p.value(0)?;
38 if p.pos != p.toks.len() {
39 return Err(p.error("expected end of input"));
40 }
41 Ok(value)
42}
43
44pub fn dson_emit(value: &Value) -> DogeResult {
47 let mut out = String::new();
48 emit(value, &mut out, 0)?;
49 Ok(Value::str(out))
50}
51
52fn dson_unicode(out: &mut String, cp: u32) {
53 let _ = write!(out, "\\u{cp:06o}");
54}
55
56fn emit(value: &Value, out: &mut String, depth: usize) -> Result<(), DogeError> {
57 if depth >= MAX_DEPTH {
58 return Err(too_deep("dson", "emit"));
59 }
60 match value {
61 Value::None => out.push_str("empty"),
62 Value::Bool(b) => out.push_str(if *b { "yes" } else { "no" }),
63 Value::Int(n) => emit_int(n, out),
64 Value::Float(x) => emit_float(*x, out)?,
65 Value::Str(s) => escape_str(s, out, dson_unicode),
68 Value::List(items) => {
69 let items = items.borrow();
70 if items.is_empty() {
71 out.push_str("so many");
72 } else {
73 out.push_str("so ");
74 for (i, item) in items.iter().enumerate() {
75 if i > 0 {
76 out.push_str(" and ");
77 }
78 emit(item, out, depth + 1)?;
79 }
80 out.push_str(" many");
81 }
82 }
83 Value::Dict(entries) => {
84 let entries = entries.borrow();
85 if entries.is_empty() {
86 out.push_str("such wow");
87 } else {
88 out.push_str("such ");
89 for (i, (key, val)) in entries.iter().enumerate() {
90 if i > 0 {
91 out.push_str(", ");
92 }
93 escape_str(key, out, dson_unicode);
94 out.push_str(" is ");
95 emit(val, out, depth + 1)?;
96 }
97 out.push_str(" wow");
98 }
99 }
100 other => return Err(unsupported("dson", other)),
101 }
102 Ok(())
103}
104
105fn emit_int(n: &BigInt, out: &mut String) {
108 out.push_str(&n.to_str_radix(8));
109}
110
111fn emit_float(x: f64, out: &mut String) -> Result<(), DogeError> {
115 if !x.is_finite() {
116 return Err(DogeError::value_error(
117 "dson.emit cannot serialize a Float that is not finite",
118 ));
119 }
120 if x.is_sign_negative() && x != 0.0 {
121 out.push('-');
122 }
123 let mut v = x.abs();
124 let int_part = v.trunc();
125 v -= int_part;
126
127 if int_part < 1.0 {
129 out.push('0');
130 } else {
131 let mut digits = Vec::new();
132 let mut ip = int_part;
133 while ip >= 1.0 {
134 digits.push(octal_digit((ip % 8.0) as u32));
135 ip = (ip / 8.0).floor();
136 }
137 out.extend(digits.iter().rev());
138 }
139
140 out.push('.');
143 if v == 0.0 {
144 out.push('0');
145 return Ok(());
146 }
147 let mut produced = 0;
148 while v != 0.0 && produced < OCTAL_FRACTION_LIMIT {
149 v *= 8.0;
150 let digit = v.floor();
151 out.push(octal_digit(digit as u32));
152 v -= digit;
153 produced += 1;
154 }
155 Ok(())
156}
157
158fn octal_digit(d: u32) -> char {
159 char::from_digit(d, 8).unwrap_or('0')
160}
161
162#[derive(Debug, Clone)]
165enum Tok {
166 Such,
167 Wow,
168 So,
169 Many,
170 Is,
171 And,
172 Also,
173 Yes,
174 No,
175 Empty,
176 Sep,
178 Str(String),
179 Num(Value),
180}
181
182fn lex(chars: &[char]) -> DogeResult<(Vec<Tok>, Vec<usize>)> {
183 let mut lexer = Lexer { chars, pos: 0 };
184 let mut toks = Vec::new();
185 let mut offs = Vec::new();
186 loop {
187 lexer.skip_ws();
188 let off = lexer.pos;
189 let Some(c) = lexer.peek() else { break };
190 let tok = match c {
191 '"' => Tok::Str(lexer.string()?),
192 '-' => lexer.number()?,
193 c if c.is_ascii_digit() => lexer.number()?,
194 ',' | '.' | '!' | '?' => {
195 lexer.pos += 1;
196 Tok::Sep
197 }
198 c if c.is_ascii_alphabetic() => lexer.word()?,
199 _ => return Err(lexer.error("unexpected character")),
200 };
201 toks.push(tok);
202 offs.push(off);
203 }
204 Ok((toks, offs))
205}
206
207struct Lexer<'a> {
208 chars: &'a [char],
209 pos: usize,
210}
211
212impl Lexer<'_> {
213 fn error(&self, what: &str) -> DogeError {
214 DogeError::value_error(format!(
215 "dson.parse: much invalid. {what} at offset {}",
216 self.pos
217 ))
218 }
219
220 fn peek(&self) -> Option<char> {
221 self.chars.get(self.pos).copied()
222 }
223
224 fn peek_at(&self, ahead: usize) -> Option<char> {
225 self.chars.get(self.pos + ahead).copied()
226 }
227
228 fn skip_ws(&mut self) {
229 while matches!(self.peek(), Some(' ' | '\t' | '\n' | '\r')) {
230 self.pos += 1;
231 }
232 }
233
234 fn starts_with(&self, word: &str) -> bool {
235 word.chars()
236 .enumerate()
237 .all(|(i, ch)| self.peek_at(i) == Some(ch))
238 }
239
240 fn word(&mut self) -> DogeResult<Tok> {
241 let start = self.pos;
242 while matches!(self.peek(), Some(c) if c.is_ascii_alphabetic()) {
243 self.pos += 1;
244 }
245 let word: String = self.chars[start..self.pos].iter().collect();
246 match word.as_str() {
247 "such" => Ok(Tok::Such),
248 "wow" => Ok(Tok::Wow),
249 "so" => Ok(Tok::So),
250 "many" => Ok(Tok::Many),
251 "is" => Ok(Tok::Is),
252 "and" => Ok(Tok::And),
253 "also" => Ok(Tok::Also),
254 "yes" => Ok(Tok::Yes),
255 "no" => Ok(Tok::No),
256 "empty" => Ok(Tok::Empty),
257 _ => Err(DogeError::value_error(format!(
258 "dson.parse: much invalid. unexpected word '{word}' at offset {start}"
259 ))),
260 }
261 }
262
263 fn string(&mut self) -> DogeResult<String> {
264 self.pos += 1; let mut s = String::new();
266 loop {
267 match self.peek() {
268 None => return Err(self.error("unterminated string")),
269 Some('"') => {
270 self.pos += 1;
271 return Ok(s);
272 }
273 Some('\\') => {
274 self.pos += 1;
275 self.escape(&mut s)?;
276 }
277 Some(c) if (c as u32) < 0x20 => {
278 return Err(self.error("control character in a string"))
279 }
280 Some(c) => {
281 s.push(c);
282 self.pos += 1;
283 }
284 }
285 }
286 }
287
288 fn escape(&mut self, s: &mut String) -> DogeResult<()> {
289 match self.peek() {
290 Some('"') => s.push('"'),
291 Some('\\') => s.push('\\'),
292 Some('/') => s.push('/'),
293 Some('b') => s.push('\u{08}'),
294 Some('f') => s.push('\u{0c}'),
295 Some('n') => s.push('\n'),
296 Some('r') => s.push('\r'),
297 Some('t') => s.push('\t'),
298 Some('u') => {
299 self.pos += 1;
300 return self.unicode_escape(s);
301 }
302 _ => return Err(self.error("invalid escape")),
303 }
304 self.pos += 1;
305 Ok(())
306 }
307
308 fn unicode_escape(&mut self, s: &mut String) -> DogeResult<()> {
311 let mut cp = 0u32;
312 for _ in 0..6 {
313 match self.peek().and_then(|c| c.to_digit(8)) {
314 Some(d) => {
315 cp = cp * 8 + d;
316 self.pos += 1;
317 }
318 None => return Err(self.error("expected six octal digits after \\u")),
319 }
320 }
321 match char::from_u32(cp) {
322 Some(c) => {
323 s.push(c);
324 Ok(())
325 }
326 None => Err(self.error("invalid code point in a \\u escape")),
327 }
328 }
329
330 fn number(&mut self) -> DogeResult<Tok> {
334 let neg = self.peek() == Some('-');
335 if neg {
336 self.pos += 1;
337 }
338 let mut int_digits = Vec::new();
339 self.octal_digits(&mut int_digits);
340 if int_digits.is_empty() {
341 return Err(self.error("expected an octal digit"));
342 }
343 if matches!(self.peek(), Some('8' | '9')) {
344 return Err(self.error("not an octal digit (DSON numbers are base 8)"));
345 }
346
347 let mut is_float = false;
348 let mut frac_digits = Vec::new();
349 if self.peek() == Some('.') && matches!(self.peek_at(1), Some('0'..='7')) {
350 is_float = true;
351 self.pos += 1; self.octal_digits(&mut frac_digits);
353 }
354
355 let mut exponent = 0i32;
356 if self.starts_with("very") || self.starts_with("VERY") {
357 is_float = true;
358 self.pos += 4;
359 let esign = match self.peek() {
360 Some('+') => {
361 self.pos += 1;
362 1
363 }
364 Some('-') => {
365 self.pos += 1;
366 -1
367 }
368 _ => 1,
369 };
370 let mut exp_digits = Vec::new();
371 self.octal_digits(&mut exp_digits);
372 if exp_digits.is_empty() {
373 return Err(self.error("expected an octal digit in the exponent"));
374 }
375 let magnitude = exp_digits.iter().fold(0i32, |acc, &d| acc * 8 + d as i32);
376 exponent = esign * magnitude;
377 }
378
379 if !is_float {
380 return Ok(Tok::Num(Value::Int(octal_to_bigint(&int_digits, neg))));
381 }
382 Ok(Tok::Num(Value::Float(octal_to_f64(
383 &int_digits,
384 &frac_digits,
385 exponent,
386 neg,
387 ))))
388 }
389
390 fn octal_digits(&mut self, out: &mut Vec<u32>) {
391 while let Some(c) = self.peek() {
392 match c.to_digit(8) {
393 Some(d) => {
394 out.push(d);
395 self.pos += 1;
396 }
397 None => break,
398 }
399 }
400 }
401}
402
403fn octal_to_bigint(digits: &[u32], neg: bool) -> BigInt {
407 let mut acc = BigInt::from(0);
408 for &d in digits {
409 acc = acc * 8 + d;
410 }
411 if neg {
412 -acc
413 } else {
414 acc
415 }
416}
417
418fn octal_to_f64(int_digits: &[u32], frac_digits: &[u32], exponent: i32, neg: bool) -> f64 {
421 let mut mag = int_digits
422 .iter()
423 .fold(0.0f64, |acc, &d| acc * 8.0 + d as f64);
424 if !frac_digits.is_empty() {
425 let frac = frac_digits
426 .iter()
427 .fold(0.0f64, |acc, &d| acc * 8.0 + d as f64);
428 mag += frac / 8f64.powi(frac_digits.len() as i32);
429 }
430 if exponent != 0 {
431 mag *= 8f64.powi(exponent);
432 }
433 if neg {
434 -mag
435 } else {
436 mag
437 }
438}
439
440struct Parser {
441 toks: Vec<Tok>,
442 offs: Vec<usize>,
443 end: usize,
444 pos: usize,
445}
446
447impl Parser {
448 fn error(&self, what: &str) -> DogeError {
449 let off = self.offs.get(self.pos).copied().unwrap_or(self.end);
450 DogeError::value_error(format!("dson.parse: much invalid. {what} at offset {off}"))
451 }
452
453 fn peek(&self) -> Option<&Tok> {
454 self.toks.get(self.pos)
455 }
456
457 fn value(&mut self, depth: usize) -> DogeResult {
458 if depth >= MAX_DEPTH {
459 return Err(too_deep("dson", "parse"));
460 }
461 match self.peek() {
462 Some(Tok::Such) => self.object(depth),
463 Some(Tok::So) => self.array(depth),
464 Some(Tok::Str(s)) => {
465 let s = s.clone();
466 self.pos += 1;
467 Ok(Value::str(s))
468 }
469 Some(Tok::Num(v)) => {
470 let v = v.clone();
471 self.pos += 1;
472 Ok(v)
473 }
474 Some(Tok::Yes) => {
475 self.pos += 1;
476 Ok(Value::Bool(true))
477 }
478 Some(Tok::No) => {
479 self.pos += 1;
480 Ok(Value::Bool(false))
481 }
482 Some(Tok::Empty) => {
483 self.pos += 1;
484 Ok(Value::None)
485 }
486 _ => Err(self.error("expected a value")),
487 }
488 }
489
490 fn object(&mut self, depth: usize) -> DogeResult {
491 self.pos += 1; let mut map = OrderedMap::new();
493 if matches!(self.peek(), Some(Tok::Wow)) {
494 self.pos += 1;
495 return Ok(Value::dict(map));
496 }
497 loop {
498 let key = match self.peek() {
499 Some(Tok::Str(s)) => {
500 let s = s.clone();
501 self.pos += 1;
502 s
503 }
504 _ => return Err(self.error("expected a string key")),
505 };
506 if !matches!(self.peek(), Some(Tok::Is)) {
507 return Err(self.error("expected 'is'"));
508 }
509 self.pos += 1;
510 let val = self.value(depth + 1)?;
511 map.insert(key, val);
512 match self.peek() {
513 Some(Tok::Sep) => self.pos += 1,
514 Some(Tok::Wow) => {
515 self.pos += 1;
516 return Ok(Value::dict(map));
517 }
518 _ => return Err(self.error("expected a separator or 'wow'")),
519 }
520 }
521 }
522
523 fn array(&mut self, depth: usize) -> DogeResult {
524 self.pos += 1; let mut items = Vec::new();
526 if matches!(self.peek(), Some(Tok::Many)) {
527 self.pos += 1;
528 return Ok(Value::list(items));
529 }
530 loop {
531 items.push(self.value(depth + 1)?);
532 match self.peek() {
533 Some(Tok::And | Tok::Also) => self.pos += 1,
534 Some(Tok::Many) => {
535 self.pos += 1;
536 return Ok(Value::list(items));
537 }
538 _ => return Err(self.error("expected 'and', 'also', or 'many'")),
539 }
540 }
541 }
542}
543
544#[cfg(test)]
545mod tests {
546 use super::*;
547 use crate::error::ErrorKind;
548 use crate::ops::values_equal;
549
550 fn parse(text: &str) -> DogeResult {
551 dson_parse(&Value::str(text))
552 }
553
554 fn emit_str(v: &Value) -> String {
555 match dson_emit(v).unwrap() {
556 Value::Str(s) => s.to_string(),
557 other => panic!("expected a Str, got {other:?}"),
558 }
559 }
560
561 #[test]
562 fn literals_map_to_doge_values() {
563 assert!(matches!(parse("yes").unwrap(), Value::Bool(true)));
564 assert!(matches!(parse("no").unwrap(), Value::Bool(false)));
565 assert!(matches!(parse("empty").unwrap(), Value::None));
566 }
567
568 #[test]
569 fn numbers_are_octal() {
570 assert!(values_equal(&parse("17620").unwrap(), &Value::int(8080)));
571 assert!(values_equal(&parse("-12").unwrap(), &Value::int(-10)));
572 assert!(matches!(parse("4very2").unwrap(), Value::Float(x) if x == 256.0));
574 assert!(matches!(parse("1very-1").unwrap(), Value::Float(x) if x == 0.125));
576 }
577
578 #[test]
579 fn octal_fractions_round_trip() {
580 assert!(matches!(parse("0.4").unwrap(), Value::Float(x) if x == 0.5));
582 assert_eq!(emit_str(&Value::Float(0.5)), "0.4");
583 assert_eq!(emit_str(&Value::Float(2.5)), "2.4");
584 assert_eq!(emit_str(&Value::int(8080)), "17620");
585 }
586
587 #[test]
588 fn a_whole_float_keeps_its_point_so_it_stays_a_float() {
589 assert_eq!(emit_str(&Value::Float(3.0)), "3.0");
590 assert!(matches!(parse("3.0").unwrap(), Value::Float(x) if x == 3.0));
591 }
592
593 #[test]
594 fn all_pair_separators_and_array_joiners_are_accepted() {
595 let d =
596 parse("such \"a\" is 1, \"b\" is 2. \"c\" is 3! \"d\" is 4? \"e\" is 5 wow").unwrap();
597 assert_eq!(
598 emit_str(&d),
599 "such \"a\" is 1, \"b\" is 2, \"c\" is 3, \"d\" is 4, \"e\" is 5 wow"
600 );
601 let a = parse("so 1 and 2 also 3 many").unwrap();
602 assert_eq!(emit_str(&a), "so 1 and 2 and 3 many");
603 }
604
605 #[test]
606 fn empty_containers_round_trip() {
607 assert_eq!(emit_str(&parse("such wow").unwrap()), "such wow");
608 assert_eq!(emit_str(&parse("so many").unwrap()), "so many");
609 }
610
611 #[test]
612 fn nested_structure_round_trips() {
613 let src = "such \"name\" is \"kabosu\", \"tags\" is so \"doge\" and \"shibe\" many, \"good\" is yes wow";
614 assert_eq!(emit_str(&parse(src).unwrap()), src);
615 }
616
617 #[test]
618 fn six_octal_digit_unicode_escape_decodes() {
619 assert!(matches!(parse("\"\\u372025\"").unwrap(), Value::Str(s) if &*s == "🐕"));
621 }
622
623 #[test]
624 fn a_non_octal_digit_is_a_catchable_error() {
625 assert_eq!(parse("18").unwrap_err().kind, ErrorKind::ValueError);
626 }
627
628 #[test]
629 fn trailing_garbage_is_a_catchable_error() {
630 assert_eq!(
631 parse("so 1 many wow").unwrap_err().kind,
632 ErrorKind::ValueError
633 );
634 }
635
636 #[test]
637 fn deep_nesting_is_a_catchable_error_not_a_stack_overflow() {
638 let deep = "so ".repeat(MAX_DEPTH + 5);
639 assert_eq!(parse(&deep).unwrap_err().kind, ErrorKind::ValueError);
640 }
641
642 #[test]
643 fn a_non_finite_float_cannot_be_emitted() {
644 assert_eq!(
645 dson_emit(&Value::Float(f64::INFINITY)).unwrap_err().kind,
646 ErrorKind::ValueError
647 );
648 }
649
650 #[test]
651 fn an_unsupported_type_is_a_catchable_type_error() {
652 let sock = Value::function(0, "f", vec![]);
653 assert_eq!(dson_emit(&sock).unwrap_err().kind, ErrorKind::TypeError);
654 }
655
656 #[test]
657 fn a_non_str_argument_to_parse_is_a_type_error() {
658 assert_eq!(
659 dson_parse(&Value::int(1)).unwrap_err().kind,
660 ErrorKind::TypeError
661 );
662 }
663}