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