Skip to main content

packr_abi/
parse.rs

1//! Value literal parser.
2//!
3//! Parses the text format produced by `Value`'s `Display` impl back into a `Value`.
4//! This enables lossless round-tripping: `Display` → `FromStr` → `Display`.
5//!
6//! Grammar (informal):
7//! ```text
8//! value     = bool | number | char | string | tuple | list
9//!           | option | result | record | variant | flags
10//! bool      = "true" | "false"
11//! number    = ["-"] digits ["." digits] suffix
12//! suffix    = "u8" | "u16" | "u32" | "u64" | "s8" | "s16" | "s32" | "s64" | "f32" | "f64"
13//! char      = "'" (escape | any) "'"
14//! string    = '"' (escape | any)* '"'
15//! escape    = "\\" | "\"" | "\n" | "\r" | "\t" | "\'"
16//! tuple     = "(" [value ("," value)* [","]] ")"
17//! list      = "[" [value ("," value)* [","]] "]"
18//! option    = "some(" value ")" | "none"
19//! result    = "ok(" value ")" | "err(" value ")"
20//! flags     = "flags(0x" hex+ ")"
21//! record    = [ident] "{" [field ("," field)* [","]] "}"
22//! field     = ident ":" value
23//! variant   = [ident] "::" ident ["(" [value ("," value)*] ")"]
24//! ident     = [a-zA-Z_] [a-zA-Z0-9_-]*
25//! ```
26
27use alloc::boxed::Box;
28use alloc::string::String;
29use alloc::vec::Vec;
30
31use crate::value::{Value, ValueType};
32
33/// Parse error with position information.
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct ParseError {
36    pub message: String,
37    pub position: usize,
38}
39
40impl core::fmt::Display for ParseError {
41    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42        write!(f, "parse error at {}: {}", self.position, self.message)
43    }
44}
45
46/// Parser state: wraps a string slice and current position.
47struct Parser<'a> {
48    input: &'a str,
49    pos: usize,
50}
51
52impl<'a> Parser<'a> {
53    fn new(input: &'a str) -> Self {
54        Self { input, pos: 0 }
55    }
56
57    fn remaining(&self) -> &'a str {
58        &self.input[self.pos..]
59    }
60
61    fn is_eof(&self) -> bool {
62        self.pos >= self.input.len()
63    }
64
65    fn peek(&self) -> Option<char> {
66        self.remaining().chars().next()
67    }
68
69    fn advance(&mut self, n: usize) {
70        self.pos += n;
71    }
72
73    fn skip_whitespace(&mut self) {
74        while let Some(c) = self.peek() {
75            if c.is_ascii_whitespace() {
76                self.advance(c.len_utf8());
77            } else {
78                break;
79            }
80        }
81    }
82
83    fn expect_char(&mut self, expected: char) -> Result<(), ParseError> {
84        match self.peek() {
85            Some(c) if c == expected => {
86                self.advance(c.len_utf8());
87                Ok(())
88            }
89            Some(c) => Err(self.error(alloc::format!("expected '{}', got '{}'", expected, c))),
90            None => Err(self.error(alloc::format!("expected '{}', got EOF", expected))),
91        }
92    }
93
94    fn starts_with(&self, s: &str) -> bool {
95        self.remaining().starts_with(s)
96    }
97
98    fn error(&self, message: String) -> ParseError {
99        ParseError {
100            message,
101            position: self.pos,
102        }
103    }
104
105    /// Parse a complete value.
106    fn parse_value(&mut self) -> Result<Value, ParseError> {
107        self.skip_whitespace();
108
109        if self.is_eof() {
110            return Err(self.error(String::from("unexpected EOF")));
111        }
112
113        match self.peek().unwrap() {
114            '"' => self.parse_string(),
115            '\'' => self.parse_char(),
116            '(' => self.parse_tuple(),
117            '[' => self.parse_list(),
118            '{' => self.parse_record_body(String::new()),
119            c if c == '-' || c.is_ascii_digit() => self.parse_number(),
120            _ => self.parse_keyword_or_named(),
121        }
122    }
123
124    fn parse_string(&mut self) -> Result<Value, ParseError> {
125        self.expect_char('"')?;
126        let mut s = String::new();
127        loop {
128            match self.peek() {
129                None => return Err(self.error(String::from("unterminated string"))),
130                Some('"') => {
131                    self.advance(1);
132                    return Ok(Value::String(s));
133                }
134                Some('\\') => {
135                    self.advance(1);
136                    match self.peek() {
137                        Some('n') => {
138                            self.advance(1);
139                            s.push('\n');
140                        }
141                        Some('r') => {
142                            self.advance(1);
143                            s.push('\r');
144                        }
145                        Some('t') => {
146                            self.advance(1);
147                            s.push('\t');
148                        }
149                        Some('\\') => {
150                            self.advance(1);
151                            s.push('\\');
152                        }
153                        Some('"') => {
154                            self.advance(1);
155                            s.push('"');
156                        }
157                        Some(c) => {
158                            return Err(self.error(alloc::format!("unknown escape '\\{}'", c)))
159                        }
160                        None => return Err(self.error(String::from("unterminated escape"))),
161                    }
162                }
163                Some(c) => {
164                    self.advance(c.len_utf8());
165                    s.push(c);
166                }
167            }
168        }
169    }
170
171    fn parse_char(&mut self) -> Result<Value, ParseError> {
172        self.expect_char('\'')?;
173        let c = match self.peek() {
174            None => return Err(self.error(String::from("unterminated char"))),
175            Some('\\') => {
176                self.advance(1);
177                match self.peek() {
178                    Some('n') => {
179                        self.advance(1);
180                        '\n'
181                    }
182                    Some('r') => {
183                        self.advance(1);
184                        '\r'
185                    }
186                    Some('t') => {
187                        self.advance(1);
188                        '\t'
189                    }
190                    Some('\\') => {
191                        self.advance(1);
192                        '\\'
193                    }
194                    Some('\'') => {
195                        self.advance(1);
196                        '\''
197                    }
198                    Some(c) => {
199                        return Err(self.error(alloc::format!("unknown char escape '\\{}'", c)))
200                    }
201                    None => return Err(self.error(String::from("unterminated char escape"))),
202                }
203            }
204            Some(c) => {
205                self.advance(c.len_utf8());
206                c
207            }
208        };
209        self.expect_char('\'')?;
210        Ok(Value::Char(c))
211    }
212
213    fn parse_number(&mut self) -> Result<Value, ParseError> {
214        let start = self.pos;
215        // Consume optional minus
216        if self.peek() == Some('-') {
217            self.advance(1);
218        }
219        // Consume digits
220        while let Some(c) = self.peek() {
221            if c.is_ascii_digit() {
222                self.advance(1);
223            } else {
224                break;
225            }
226        }
227        // Check for decimal point
228        let has_dot = if self.peek() == Some('.') {
229            self.advance(1);
230            while let Some(c) = self.peek() {
231                if c.is_ascii_digit() {
232                    self.advance(1);
233                } else {
234                    break;
235                }
236            }
237            true
238        } else {
239            false
240        };
241
242        let num_str = &self.input[start..self.pos];
243
244        // Parse type suffix
245        let remaining = self.remaining();
246        let suffixes: &[(&str, u8)] = &[
247            ("u8", 1),
248            ("u16", 2),
249            ("u32", 3),
250            ("u64", 4),
251            ("s8", 5),
252            ("s16", 6),
253            ("s32", 7),
254            ("s64", 8),
255            ("f32", 9),
256            ("f64", 10),
257        ];
258
259        for &(suffix, id) in suffixes {
260            if remaining.starts_with(suffix) {
261                // Make sure suffix isn't followed by alphanumeric (e.g. "u320" shouldn't match "u32")
262                let after = remaining
263                    .get(suffix.len()..suffix.len() + 1)
264                    .and_then(|s| s.chars().next());
265                if after.is_none_or(|c| !c.is_ascii_alphanumeric()) {
266                    self.advance(suffix.len());
267                    return self.make_number(num_str, id, has_dot);
268                }
269            }
270        }
271
272        Err(self.error(alloc::format!(
273            "number '{}' missing type suffix (e.g. u32, s64, f32)",
274            num_str
275        )))
276    }
277
278    fn make_number(
279        &self,
280        num_str: &str,
281        suffix_id: u8,
282        _has_dot: bool,
283    ) -> Result<Value, ParseError> {
284        let pos = self.pos;
285        let err = |msg: String| ParseError {
286            message: msg,
287            position: pos,
288        };
289
290        match suffix_id {
291            1 => {
292                // u8
293                let v: u8 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
294                Ok(Value::U8(v))
295            }
296            2 => {
297                // u16
298                let v: u16 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
299                Ok(Value::U16(v))
300            }
301            3 => {
302                // u32
303                let v: u32 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
304                Ok(Value::U32(v))
305            }
306            4 => {
307                // u64
308                let v: u64 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
309                Ok(Value::U64(v))
310            }
311            5 => {
312                // s8
313                let v: i8 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
314                Ok(Value::S8(v))
315            }
316            6 => {
317                // s16
318                let v: i16 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
319                Ok(Value::S16(v))
320            }
321            7 => {
322                // s32
323                let v: i32 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
324                Ok(Value::S32(v))
325            }
326            8 => {
327                // s64
328                let v: i64 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
329                Ok(Value::S64(v))
330            }
331            9 => {
332                // f32
333                let v: f32 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
334                Ok(Value::F32(v))
335            }
336            10 => {
337                // f64
338                let v: f64 = num_str.parse().map_err(|e| err(alloc::format!("{}", e)))?;
339                Ok(Value::F64(v))
340            }
341            _ => Err(err(String::from("invalid suffix id"))),
342        }
343    }
344
345    fn parse_tuple(&mut self) -> Result<Value, ParseError> {
346        self.expect_char('(')?;
347        self.skip_whitespace();
348        if self.peek() == Some(')') {
349            self.advance(1);
350            return Ok(Value::Tuple(Vec::new()));
351        }
352        let items = self.parse_comma_list(')')?;
353        Ok(Value::Tuple(items))
354    }
355
356    fn parse_list(&mut self) -> Result<Value, ParseError> {
357        self.expect_char('[')?;
358        self.skip_whitespace();
359        if self.peek() == Some(']') {
360            self.advance(1);
361            // Empty list — must be annotated with type: []<TYPE>
362            self.skip_whitespace();
363            let elem_type = if self.peek() == Some('<') {
364                self.advance(1);
365                let t = self.parse_value_type()?;
366                self.expect_char('>')?;
367                t
368            } else {
369                return Err(self.error(String::from(
370                    "empty list requires type annotation: []<TYPE>",
371                )));
372            };
373            return Ok(Value::List {
374                elem_type,
375                items: Vec::new(),
376            });
377        }
378        let items = self.parse_comma_list(']')?;
379        let elem_type = items
380            .first()
381            .map(|v| v.infer_type())
382            .unwrap_or(ValueType::S32);
383        Ok(Value::List { elem_type, items })
384    }
385
386    /// Parse a ValueType (e.g. `u8`, `list<u8>`, `option<string>`, `result<u32, string>`,
387    /// `tuple<u8, string>`, `record-name`, `variant-name`).
388    fn parse_value_type(&mut self) -> Result<ValueType, ParseError> {
389        self.skip_whitespace();
390        // Try built-in primitive types and compound types
391        let primitives: &[(&str, ValueType)] = &[
392            ("bool", ValueType::Bool),
393            ("u8", ValueType::U8),
394            ("u16", ValueType::U16),
395            ("u32", ValueType::U32),
396            ("u64", ValueType::U64),
397            ("s8", ValueType::S8),
398            ("s16", ValueType::S16),
399            ("s32", ValueType::S32),
400            ("s64", ValueType::S64),
401            ("f32", ValueType::F32),
402            ("f64", ValueType::F64),
403            ("char", ValueType::Char),
404            ("string", ValueType::String),
405            ("flags", ValueType::Flags),
406        ];
407        for (name, ty) in primitives {
408            if self.starts_with(name)
409                && !self.is_ident_continue_at(name.len())
410                && self
411                    .remaining()
412                    .get(name.len()..name.len() + 1)
413                    .is_none_or(|s| s != "<")
414            {
415                self.advance(name.len());
416                return Ok(ty.clone());
417            }
418        }
419        if self.starts_with("list<") {
420            self.advance(5);
421            let inner = self.parse_value_type()?;
422            self.skip_whitespace();
423            self.expect_char('>')?;
424            return Ok(ValueType::List(Box::new(inner)));
425        }
426        if self.starts_with("option<") {
427            self.advance(7);
428            let inner = self.parse_value_type()?;
429            self.skip_whitespace();
430            self.expect_char('>')?;
431            return Ok(ValueType::Option(Box::new(inner)));
432        }
433        if self.starts_with("result<") {
434            self.advance(7);
435            let ok = self.parse_value_type()?;
436            self.skip_whitespace();
437            self.expect_char(',')?;
438            self.skip_whitespace();
439            let err = self.parse_value_type()?;
440            self.skip_whitespace();
441            self.expect_char('>')?;
442            return Ok(ValueType::Result {
443                ok: Box::new(ok),
444                err: Box::new(err),
445            });
446        }
447        if self.starts_with("tuple<") {
448            self.advance(6);
449            self.skip_whitespace();
450            // Handle empty tuple: tuple<>
451            if self.peek() == Some('>') {
452                self.advance(1);
453                return Ok(ValueType::Tuple(Vec::new()));
454            }
455            let mut types = Vec::new();
456            loop {
457                self.skip_whitespace();
458                types.push(self.parse_value_type()?);
459                self.skip_whitespace();
460                match self.peek() {
461                    Some(',') => {
462                        self.advance(1);
463                    }
464                    Some('>') => {
465                        self.advance(1);
466                        return Ok(ValueType::Tuple(types));
467                    }
468                    Some(c) => {
469                        return Err(self.error(alloc::format!("expected ',' or '>', got '{}'", c)))
470                    }
471                    None => return Err(self.error(String::from("expected '>', got EOF"))),
472                }
473            }
474        }
475        // Otherwise an identifier — record or variant name
476        // We can't distinguish them syntactically; default to Record (could be wrong, but
477        // type names round-trip the same way regardless).
478        let name = self.parse_ident()?;
479        Ok(ValueType::Record(name))
480    }
481
482    /// Parse comma-separated values until `end_char`.
483    fn parse_comma_list(&mut self, end_char: char) -> Result<Vec<Value>, ParseError> {
484        let mut items = Vec::new();
485        loop {
486            self.skip_whitespace();
487            if self.peek() == Some(end_char) {
488                self.advance(1);
489                return Ok(items);
490            }
491            items.push(self.parse_value()?);
492            self.skip_whitespace();
493            match self.peek() {
494                Some(',') => {
495                    self.advance(1);
496                }
497                Some(c) if c == end_char => {}
498                Some(c) => {
499                    return Err(self.error(alloc::format!(
500                        "expected ',' or '{}', got '{}'",
501                        end_char,
502                        c
503                    )))
504                }
505                None => return Err(self.error(alloc::format!("expected '{}', got EOF", end_char))),
506            }
507        }
508    }
509
510    /// Parse keywords (true, false, none, some, ok, err, flags) or named things (records, variants).
511    fn parse_keyword_or_named(&mut self) -> Result<Value, ParseError> {
512        // Check for keywords first
513        if self.starts_with("true") && !self.is_ident_continue_at(4) {
514            self.advance(4);
515            return Ok(Value::Bool(true));
516        }
517        if self.starts_with("false") && !self.is_ident_continue_at(5) {
518            self.advance(5);
519            return Ok(Value::Bool(false));
520        }
521        if self.starts_with("none<") {
522            self.advance(5);
523            let inner_type = self.parse_value_type()?;
524            self.skip_whitespace();
525            self.expect_char('>')?;
526            return Ok(Value::Option {
527                inner_type,
528                value: None,
529            });
530        }
531        if self.starts_with("some<") {
532            self.advance(5);
533            let inner_type = self.parse_value_type()?;
534            self.skip_whitespace();
535            self.expect_char('>')?;
536            self.expect_char('(')?;
537            let inner = self.parse_value()?;
538            self.skip_whitespace();
539            self.expect_char(')')?;
540            return Ok(Value::Option {
541                inner_type,
542                value: Some(Box::new(inner)),
543            });
544        }
545        if self.starts_with("ok<") {
546            self.advance(3);
547            let ok_type = self.parse_value_type()?;
548            self.skip_whitespace();
549            self.expect_char(',')?;
550            self.skip_whitespace();
551            let err_type = self.parse_value_type()?;
552            self.skip_whitespace();
553            self.expect_char('>')?;
554            self.expect_char('(')?;
555            let inner = self.parse_value()?;
556            self.skip_whitespace();
557            self.expect_char(')')?;
558            return Ok(Value::Result {
559                ok_type,
560                err_type,
561                value: Ok(Box::new(inner)),
562            });
563        }
564        if self.starts_with("err<") {
565            self.advance(4);
566            let ok_type = self.parse_value_type()?;
567            self.skip_whitespace();
568            self.expect_char(',')?;
569            self.skip_whitespace();
570            let err_type = self.parse_value_type()?;
571            self.skip_whitespace();
572            self.expect_char('>')?;
573            self.expect_char('(')?;
574            let inner = self.parse_value()?;
575            self.skip_whitespace();
576            self.expect_char(')')?;
577            return Ok(Value::Result {
578                ok_type,
579                err_type,
580                value: Err(Box::new(inner)),
581            });
582        }
583        if self.starts_with("flags(0x") {
584            self.advance(8);
585            let start = self.pos;
586            while let Some(c) = self.peek() {
587                if c.is_ascii_hexdigit() {
588                    self.advance(1);
589                } else {
590                    break;
591                }
592            }
593            let hex_str = &self.input[start..self.pos];
594            let v = u64::from_str_radix(hex_str, 16)
595                .map_err(|e| self.error(alloc::format!("invalid flags hex: {}", e)))?;
596            self.expect_char(')')?;
597            return Ok(Value::Flags(v));
598        }
599
600        // Must be an identifier — could be record or variant
601        // Also handle leading :: for empty-type-name variants
602        if self.starts_with("::") {
603            self.advance(2);
604            let case_name = self.parse_ident()?;
605            let payload = self.parse_optional_payload()?;
606            return Ok(Value::Variant {
607                type_name: String::new(),
608                case_name,
609                tag: 0,
610                payload,
611            });
612        }
613
614        let ident = self.parse_ident()?;
615        self.skip_whitespace();
616
617        match self.peek() {
618            Some('{') => self.parse_record_body(ident),
619            Some(':') if self.starts_with("::") => {
620                self.advance(2);
621                let case_name = self.parse_ident()?;
622                let payload = self.parse_optional_payload()?;
623                Ok(Value::Variant {
624                    type_name: ident,
625                    case_name,
626                    tag: 0,
627                    payload,
628                })
629            }
630            _ => Err(self.error(alloc::format!(
631                "unexpected identifier '{}' (expected {{ or ::)",
632                ident
633            ))),
634        }
635    }
636
637    fn parse_record_body(&mut self, type_name: String) -> Result<Value, ParseError> {
638        self.expect_char('{')?;
639        self.skip_whitespace();
640        if self.peek() == Some('}') {
641            self.advance(1);
642            return Ok(Value::Record {
643                type_name,
644                fields: Vec::new(),
645            });
646        }
647        let mut fields = Vec::new();
648        loop {
649            self.skip_whitespace();
650            if self.peek() == Some('}') {
651                self.advance(1);
652                return Ok(Value::Record { type_name, fields });
653            }
654            let name = self.parse_ident()?;
655            self.skip_whitespace();
656            self.expect_char(':')?;
657            self.skip_whitespace();
658            let value = self.parse_value()?;
659            fields.push((name, value));
660            self.skip_whitespace();
661            match self.peek() {
662                Some(',') => {
663                    self.advance(1);
664                }
665                Some('}') => {}
666                Some(c) => {
667                    return Err(self.error(alloc::format!("expected ',' or '}}', got '{}'", c)))
668                }
669                None => return Err(self.error(String::from("expected '}', got EOF"))),
670            }
671        }
672    }
673
674    fn parse_optional_payload(&mut self) -> Result<Vec<Value>, ParseError> {
675        self.skip_whitespace();
676        if self.peek() == Some('(') {
677            self.advance(1);
678            self.skip_whitespace();
679            if self.peek() == Some(')') {
680                self.advance(1);
681                return Ok(Vec::new());
682            }
683            let items = self.parse_comma_list(')')?;
684            Ok(items)
685        } else {
686            Ok(Vec::new())
687        }
688    }
689
690    fn parse_ident(&mut self) -> Result<String, ParseError> {
691        let start = self.pos;
692        match self.peek() {
693            Some(c) if c.is_ascii_alphabetic() || c == '_' => {
694                self.advance(c.len_utf8());
695            }
696            _ => return Err(self.error(String::from("expected identifier"))),
697        }
698        while let Some(c) = self.peek() {
699            if c.is_ascii_alphanumeric() || c == '_' || c == '-' {
700                self.advance(c.len_utf8());
701            } else {
702                break;
703            }
704        }
705        Ok(String::from(&self.input[start..self.pos]))
706    }
707
708    /// Check if there's an identifier-continue character at offset from current position.
709    fn is_ident_continue_at(&self, offset: usize) -> bool {
710        self.remaining()
711            .get(offset..offset + 1)
712            .and_then(|s| s.chars().next())
713            .is_some_and(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
714    }
715}
716
717/// Parse a value literal string into a `Value`.
718pub fn parse_value(input: &str) -> Result<Value, ParseError> {
719    let mut parser = Parser::new(input);
720    let value = parser.parse_value()?;
721    parser.skip_whitespace();
722    if !parser.is_eof() {
723        return Err(parser.error(alloc::format!("trailing input: '{}'", parser.remaining())));
724    }
725    Ok(value)
726}
727
728impl core::str::FromStr for Value {
729    type Err = ParseError;
730
731    fn from_str(s: &str) -> Result<Self, Self::Err> {
732        parse_value(s)
733    }
734}
735
736#[cfg(test)]
737mod tests {
738    use super::*;
739    use alloc::string::ToString;
740    use alloc::vec;
741
742    #[test]
743    fn test_bool() {
744        assert_eq!(parse_value("true").unwrap(), Value::Bool(true));
745        assert_eq!(parse_value("false").unwrap(), Value::Bool(false));
746    }
747
748    #[test]
749    fn test_numbers() {
750        assert_eq!(parse_value("42u8").unwrap(), Value::U8(42));
751        assert_eq!(parse_value("1000u16").unwrap(), Value::U16(1000));
752        assert_eq!(parse_value("123456u32").unwrap(), Value::U32(123456));
753        assert_eq!(parse_value("99u64").unwrap(), Value::U64(99));
754        assert_eq!(parse_value("-5s8").unwrap(), Value::S8(-5));
755        assert_eq!(parse_value("-100s16").unwrap(), Value::S16(-100));
756        assert_eq!(parse_value("0s32").unwrap(), Value::S32(0));
757        assert_eq!(parse_value("-1s64").unwrap(), Value::S64(-1));
758        assert_eq!(parse_value("3.14f32").unwrap(), Value::F32(3.14));
759        assert_eq!(parse_value("2.718f64").unwrap(), Value::F64(2.718));
760        assert_eq!(parse_value("1.0f32").unwrap(), Value::F32(1.0));
761    }
762
763    #[test]
764    fn test_string() {
765        assert_eq!(
766            parse_value("\"hello\"").unwrap(),
767            Value::String(String::from("hello"))
768        );
769        assert_eq!(
770            parse_value("\"a\\nb\"").unwrap(),
771            Value::String(String::from("a\nb"))
772        );
773        assert_eq!(
774            parse_value("\"a\\\"b\"").unwrap(),
775            Value::String(String::from("a\"b"))
776        );
777        assert_eq!(parse_value("\"\"").unwrap(), Value::String(String::new()));
778    }
779
780    #[test]
781    fn test_char() {
782        assert_eq!(parse_value("'x'").unwrap(), Value::Char('x'));
783        assert_eq!(parse_value("'\\n'").unwrap(), Value::Char('\n'));
784        assert_eq!(parse_value("'\\''").unwrap(), Value::Char('\''));
785    }
786
787    #[test]
788    fn test_tuple() {
789        assert_eq!(parse_value("()").unwrap(), Value::Tuple(Vec::new()));
790        assert_eq!(
791            parse_value("(42u32, \"hi\")").unwrap(),
792            Value::Tuple(vec![Value::U32(42), Value::String(String::from("hi"))])
793        );
794    }
795
796    #[test]
797    fn test_list() {
798        assert_eq!(
799            parse_value("[]<u8>").unwrap(),
800            Value::List {
801                elem_type: ValueType::U8,
802                items: Vec::new()
803            }
804        );
805        assert_eq!(
806            parse_value("[1u8, 2u8, 3u8]").unwrap(),
807            Value::List {
808                elem_type: ValueType::U8,
809                items: vec![Value::U8(1), Value::U8(2), Value::U8(3)]
810            }
811        );
812    }
813
814    #[test]
815    fn test_option() {
816        assert_eq!(
817            parse_value("none<u32>").unwrap(),
818            Value::Option {
819                inner_type: ValueType::U32,
820                value: None
821            }
822        );
823        assert_eq!(
824            parse_value("none<list<u8>>").unwrap(),
825            Value::Option {
826                inner_type: ValueType::List(Box::new(ValueType::U8)),
827                value: None
828            }
829        );
830        assert_eq!(
831            parse_value("some<u32>(42u32)").unwrap(),
832            Value::Option {
833                inner_type: ValueType::U32,
834                value: Some(Box::new(Value::U32(42)))
835            }
836        );
837    }
838
839    #[test]
840    fn test_result() {
841        assert_eq!(
842            parse_value("ok<u32, string>(1u32)").unwrap(),
843            Value::Result {
844                ok_type: ValueType::U32,
845                err_type: ValueType::String,
846                value: Ok(Box::new(Value::U32(1)))
847            }
848        );
849        assert_eq!(
850            parse_value("err<u32, string>(\"bad\")").unwrap(),
851            Value::Result {
852                ok_type: ValueType::U32,
853                err_type: ValueType::String,
854                value: Err(Box::new(Value::String(String::from("bad"))))
855            }
856        );
857    }
858
859    #[test]
860    fn test_record() {
861        let v = parse_value("actor-state{greeting: \"Hello\", count: 0u32}").unwrap();
862        assert_eq!(
863            v,
864            Value::Record {
865                type_name: String::from("actor-state"),
866                fields: vec![
867                    (
868                        String::from("greeting"),
869                        Value::String(String::from("Hello"))
870                    ),
871                    (String::from("count"), Value::U32(0)),
872                ],
873            }
874        );
875    }
876
877    #[test]
878    fn test_variant() {
879        let v = parse_value("color::rgb(255u8, 0u8, 128u8)").unwrap();
880        assert_eq!(
881            v,
882            Value::Variant {
883                type_name: String::from("color"),
884                case_name: String::from("rgb"),
885                tag: 0,
886                payload: vec![Value::U8(255), Value::U8(0), Value::U8(128)],
887            }
888        );
889
890        // No payload
891        let v = parse_value("status::active").unwrap();
892        assert_eq!(
893            v,
894            Value::Variant {
895                type_name: String::from("status"),
896                case_name: String::from("active"),
897                tag: 0,
898                payload: Vec::new(),
899            }
900        );
901    }
902
903    #[test]
904    fn test_variant_empty_type() {
905        let v = parse_value("::my-case(1u32)").unwrap();
906        assert_eq!(
907            v,
908            Value::Variant {
909                type_name: String::new(),
910                case_name: String::from("my-case"),
911                tag: 0,
912                payload: vec![Value::U32(1)],
913            }
914        );
915    }
916
917    #[test]
918    fn test_flags() {
919        assert_eq!(parse_value("flags(0xff)").unwrap(), Value::Flags(0xff));
920        assert_eq!(parse_value("flags(0x0)").unwrap(), Value::Flags(0));
921    }
922
923    #[test]
924    fn test_round_trip() {
925        let values = vec![
926            Value::Bool(true),
927            Value::U32(42),
928            Value::S64(-100),
929            Value::F32(3.14),
930            Value::String(String::from("hello \"world\"\nbye")),
931            Value::Char('\t'),
932            Value::Tuple(vec![Value::U8(1), Value::Bool(false)]),
933            Value::List {
934                elem_type: ValueType::S32,
935                items: vec![Value::S32(1), Value::S32(2)],
936            },
937            Value::Option {
938                inner_type: ValueType::String,
939                value: Some(Box::new(Value::String(String::from("hi")))),
940            },
941            Value::Option {
942                inner_type: ValueType::S32,
943                value: None,
944            },
945            Value::Result {
946                ok_type: ValueType::U32,
947                err_type: ValueType::String,
948                value: Ok(Box::new(Value::U32(99))),
949            },
950            Value::Record {
951                type_name: String::from("point"),
952                fields: vec![
953                    (String::from("x"), Value::S32(10)),
954                    (String::from("y"), Value::S32(20)),
955                ],
956            },
957            Value::Variant {
958                type_name: String::from("expr"),
959                case_name: String::from("num"),
960                tag: 1,
961                payload: vec![Value::S64(42)],
962            },
963            Value::Flags(0xdeadbeef),
964        ];
965
966        for val in &values {
967            let text = alloc::format!("{}", val);
968            let parsed = parse_value(&text).unwrap_or_else(|e| {
969                panic!("Failed to parse '{}': {}", text, e);
970            });
971            // Compare Display output (tag field may differ but Display should match)
972            let reparsed_text = alloc::format!("{}", parsed);
973            assert_eq!(text, reparsed_text, "Round-trip failed for: {}", text);
974        }
975    }
976
977    #[test]
978    fn test_nested_record() {
979        let input = "outer{inner: point{x: 1s32, y: 2s32}, name: \"test\"}";
980        let v = parse_value(input).unwrap();
981        let output = alloc::format!("{}", v);
982        assert_eq!(input, output);
983    }
984
985    #[test]
986    fn test_cgrf_round_trip() {
987        // Verify that Display → Parse → Encode produces identical CGRF bytes
988        // (which means identical hashes — critical for chain replay).
989        let values = vec![
990            Value::Option {
991                inner_type: ValueType::List(Box::new(ValueType::U8)),
992                value: None,
993            },
994            Value::Result {
995                ok_type: ValueType::Tuple(vec![]),
996                err_type: ValueType::String,
997                value: Ok(Box::new(Value::Tuple(vec![]))),
998            },
999            Value::List {
1000                elem_type: ValueType::U8,
1001                items: Vec::new(),
1002            },
1003            Value::Record {
1004                type_name: String::from("host-function-call"),
1005                fields: vec![
1006                    (
1007                        String::from("interface"),
1008                        Value::String(String::from("theater:simple/runtime")),
1009                    ),
1010                    (
1011                        String::from("function"),
1012                        Value::String(String::from("shutdown")),
1013                    ),
1014                    (
1015                        String::from("input"),
1016                        Value::Option {
1017                            inner_type: ValueType::List(Box::new(ValueType::U8)),
1018                            value: None,
1019                        },
1020                    ),
1021                    (
1022                        String::from("output"),
1023                        Value::Result {
1024                            ok_type: ValueType::Tuple(vec![]),
1025                            err_type: ValueType::String,
1026                            value: Ok(Box::new(Value::Tuple(vec![]))),
1027                        },
1028                    ),
1029                ],
1030            },
1031        ];
1032
1033        for val in &values {
1034            let original_bytes = crate::encode(val).unwrap();
1035            let text = alloc::format!("{}", val);
1036            let parsed = parse_value(&text).unwrap_or_else(|e| {
1037                panic!("Failed to parse '{}': {}", text, e);
1038            });
1039            let reparsed_bytes = crate::encode(&parsed).unwrap();
1040            assert_eq!(
1041                original_bytes, reparsed_bytes,
1042                "CGRF bytes differ after round-trip for: {}",
1043                text
1044            );
1045        }
1046    }
1047
1048    #[test]
1049    fn test_trailing_comma() {
1050        // Trailing commas are accepted
1051        assert_eq!(
1052            parse_value("(1u32, 2u32,)").unwrap(),
1053            Value::Tuple(vec![Value::U32(1), Value::U32(2)])
1054        );
1055        assert_eq!(
1056            parse_value("[1u8,]").unwrap(),
1057            Value::List {
1058                elem_type: ValueType::U8,
1059                items: vec![Value::U8(1)]
1060            }
1061        );
1062    }
1063}