snarkvm_console_program/data/plaintext/
parse.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17
18impl<N: Network> Parser for Plaintext<N> {
19    /// Parses a string into a plaintext value.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        // Parse the string into a plaintext value.
23        Self::parse_internal(string, 0)
24    }
25}
26
27impl<N: Network> Plaintext<N> {
28    /// Parses a sanitized pair: `identifier: plaintext`.
29    fn parse_pair(string: &str, depth: usize) -> ParserResult<(Identifier<N>, Self)> {
30        // Parse the whitespace and comments from the string.
31        let (string, _) = Sanitizer::parse(string)?;
32        // Parse the identifier from the string.
33        let (string, identifier) = Identifier::parse(string)?;
34        // Parse the whitespace from the string.
35        let (string, _) = Sanitizer::parse_whitespaces(string)?;
36        // Parse the ":" from the string.
37        let (string, _) = tag(":")(string)?;
38        // Parse the plaintext from the string.
39        let (string, plaintext) = Self::parse_internal(string, depth + 1)?;
40        // Parse the whitespace from the string.
41        let (string, _) = Sanitizer::parse_whitespaces(string)?;
42        // Return the identifier and plaintext.
43        Ok((string, (identifier, plaintext)))
44    }
45
46    /// Parses a plaintext as a struct: `{ identifier_0: plaintext_0, ..., identifier_n: plaintext_n }`.
47    fn parse_struct(string: &str, depth: usize) -> ParserResult<Self> {
48        // Parse the whitespace and comments from the string.
49        let (string, _) = Sanitizer::parse(string)?;
50        // Parse the "{" from the string.
51        let (string, _) = tag("{")(string)?;
52        // Parse the members.
53        let (string, members) =
54            map_res(separated_list1(tag(","), |input| Self::parse_pair(input, depth)), |members: Vec<_>| {
55                // Ensure the members has no duplicate names.
56                if has_duplicates(members.iter().map(|(name, ..)| name)) {
57                    return Err(error("Duplicate member in struct"));
58                }
59                // Ensure the number of structs is within the maximum limit.
60                match members.len() <= N::MAX_STRUCT_ENTRIES {
61                    true => Ok(members),
62                    false => Err(error(format!("Found a plaintext that exceeds size ({})", members.len()))),
63                }
64            })(string)?;
65        // Parse the whitespace and comments from the string.
66        let (string, _) = Sanitizer::parse(string)?;
67        // Parse the '}' from the string.
68        let (string, _) = tag("}")(string)?;
69        // Output the plaintext.
70        Ok((string, Self::Struct(IndexMap::from_iter(members), Default::default())))
71    }
72
73    /// Parses a plaintext as an array: `[plaintext_0, ..., plaintext_n]`.
74    fn parse_array(string: &str, depth: usize) -> ParserResult<Self> {
75        // Parse the whitespace and comments from the string.
76        let (string, _) = Sanitizer::parse(string)?;
77        // Parse the "[" from the string.
78        let (string, _) = tag("[")(string)?;
79        // Parse the members.
80        let (string, members) = separated_list1(tag(","), |input| Self::parse_internal(input, depth + 1))(string)?;
81        // Parse the whitespace and comments from the string.
82        let (string, _) = Sanitizer::parse(string)?;
83        // Parse the ']' from the string.
84        let (string, _) = tag("]")(string)?;
85        // Output the plaintext.
86        Ok((string, Self::Array(members, Default::default())))
87    }
88
89    /// Parses a string into a plaintext value, while tracking the depth of the data.
90    fn parse_internal(string: &str, depth: usize) -> ParserResult<Self> {
91        // Ensure that the depth is within the maximum limit.
92        if depth > N::MAX_DATA_DEPTH {
93            return map_res(take(0usize), |_| {
94                Err(error(format!("Found a plaintext that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
95            })(string);
96        }
97        // Parse the whitespace and comments from the string.
98        let (string, _) = Sanitizer::parse(string)?;
99        // Parse the struct or array from the string.
100        // Parse to determine the plaintext (order matters).
101        alt((
102            // Parse a plaintext literal.
103            map(Literal::parse, |literal| Self::Literal(literal, Default::default())),
104            // Parse a plaintext struct.
105            |input| Self::parse_struct(input, depth),
106            // Parse a plaintext array.
107            |input| Self::parse_array(input, depth),
108        ))(string)
109    }
110}
111
112impl<N: Network> FromStr for Plaintext<N> {
113    type Err = Error;
114
115    /// Returns a plaintext from a string literal.
116    fn from_str(string: &str) -> Result<Self> {
117        match Self::parse(string) {
118            Ok((remainder, object)) => {
119                // Ensure the remainder is empty.
120                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
121                // Return the object.
122                Ok(object)
123            }
124            Err(error) => bail!("Failed to parse string. {error}"),
125        }
126    }
127}
128
129impl<N: Network> Debug for Plaintext<N> {
130    /// Prints the plaintext as a string.
131    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
132        Display::fmt(self, f)
133    }
134}
135
136impl<N: Network> Display for Plaintext<N> {
137    /// Prints the plaintext as a string.
138    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
139        self.fmt_internal(f, 0)
140    }
141}
142
143impl<N: Network> Plaintext<N> {
144    /// Prints the plaintext with the given indentation depth.
145    fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
146        /// The number of spaces to indent.
147        const INDENT: usize = 2;
148
149        match self {
150            // Prints the literal, i.e. 10field
151            Self::Literal(literal, ..) => write!(f, "{:indent$}{literal}", "", indent = depth * INDENT),
152            // Prints the struct, i.e. { first: 10i64, second: 198u64 }
153            Self::Struct(struct_, ..) => {
154                // Print the opening brace.
155                write!(f, "{{")?;
156                // Print the members.
157                struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
158                    match plaintext {
159                        Self::Literal(literal, ..) => match i == struct_.len() - 1 {
160                            true => {
161                                // Print the last member without a comma.
162                                write!(f, "\n{:indent$}{name}: {literal}", "", indent = (depth + 1) * INDENT)?;
163                                // Print the closing brace.
164                                write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
165                            }
166                            // Print the member with a comma.
167                            false => write!(f, "\n{:indent$}{name}: {literal},", "", indent = (depth + 1) * INDENT),
168                        },
169                        Self::Struct(..) | Self::Array(..) => {
170                            // Print the member name.
171                            write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
172                            // Print the member.
173                            plaintext.fmt_internal(f, depth + 1)?;
174                            // Print the closing brace.
175                            match i == struct_.len() - 1 {
176                                // Print the last member without a comma.
177                                true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
178                                // Print the member with a comma.
179                                false => write!(f, ","),
180                            }
181                        }
182                    }
183                })
184            }
185            // Prints the array, i.e. [ 10u64, 198u64 ]
186            Self::Array(array, ..) => {
187                // Print the opening bracket.
188                write!(f, "[")?;
189                // Print the members.
190                array.iter().enumerate().try_for_each(|(i, plaintext)| {
191                    match plaintext {
192                        Self::Literal(literal, ..) => match i == array.len() - 1 {
193                            true => {
194                                // Print the last member without a comma.
195                                write!(f, "\n{:indent$}{literal}", "", indent = (depth + 1) * INDENT)?;
196                                // Print the closing bracket.
197                                write!(f, "\n{:indent$}]", "", indent = depth * INDENT)
198                            }
199                            // Print the member with a comma.
200                            false => write!(f, "\n{:indent$}{literal},", "", indent = (depth + 1) * INDENT),
201                        },
202                        Self::Struct(..) | Self::Array(..) => {
203                            // Print a newline.
204                            write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT)?;
205                            // Print the member.
206                            plaintext.fmt_internal(f, depth + 1)?;
207                            // Print the closing brace.
208                            match i == array.len() - 1 {
209                                // Print the last member without a comma.
210                                true => write!(f, "\n{:indent$}]", "", indent = depth * INDENT),
211                                // Print the member with a comma.
212                                false => write!(f, ","),
213                            }
214                        }
215                    }
216                })
217            }
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use snarkvm_console_network::MainnetV0;
226
227    type CurrentNetwork = MainnetV0;
228
229    #[test]
230    fn test_parse_literal() -> Result<()> {
231        // Sanity check.
232        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("5u8")?;
233        assert_eq!("5u8", candidate.to_string());
234        assert_eq!("", remainder);
235
236        Ok(())
237    }
238
239    #[test]
240    fn test_parse_struct() -> Result<()> {
241        // Sanity check.
242        let expected = r"{
243  foo: 5u8
244}";
245        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("{ foo: 5u8 }")?;
246        assert_eq!(expected, candidate.to_string());
247        assert_eq!("", remainder);
248
249        let expected = r"{
250  foo: 5u8,
251  bar: {
252    baz: 10field,
253    qux: {
254      quux: {
255        corge: {
256          grault: {
257            garply: {
258              waldo: {
259                fred: {
260                  plugh: {
261                    xyzzy: {
262                      thud: true
263                    }
264                  }
265                }
266              }
267            }
268          }
269        }
270      }
271    }
272  }
273}";
274        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(
275            "{ foo: 5u8, bar: { baz: 10field, qux: {quux:{corge :{grault:  {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true}} }}}  }}}}}}",
276        )?;
277        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
278        assert_eq!(expected, candidate.to_string());
279        assert_eq!("", remainder);
280
281        Ok(())
282    }
283
284    #[test]
285    fn test_parse_fails() {
286        // Must be non-empty.
287        assert!(Plaintext::<CurrentNetwork>::parse("").is_err());
288        assert!(Plaintext::<CurrentNetwork>::parse("{}").is_err());
289
290        // Invalid characters.
291        assert!(Plaintext::<CurrentNetwork>::parse("_").is_err());
292        assert!(Plaintext::<CurrentNetwork>::parse("__").is_err());
293        assert!(Plaintext::<CurrentNetwork>::parse("___").is_err());
294        assert!(Plaintext::<CurrentNetwork>::parse("-").is_err());
295        assert!(Plaintext::<CurrentNetwork>::parse("--").is_err());
296        assert!(Plaintext::<CurrentNetwork>::parse("---").is_err());
297        assert!(Plaintext::<CurrentNetwork>::parse("*").is_err());
298        assert!(Plaintext::<CurrentNetwork>::parse("**").is_err());
299        assert!(Plaintext::<CurrentNetwork>::parse("***").is_err());
300
301        // Must not start with a number.
302        assert!(Plaintext::<CurrentNetwork>::parse("1").is_err());
303        assert!(Plaintext::<CurrentNetwork>::parse("2").is_err());
304        assert!(Plaintext::<CurrentNetwork>::parse("3").is_err());
305        assert!(Plaintext::<CurrentNetwork>::parse("1foo").is_err());
306        assert!(Plaintext::<CurrentNetwork>::parse("12").is_err());
307        assert!(Plaintext::<CurrentNetwork>::parse("111").is_err());
308
309        // Must fit within the data capacity of a base field element.
310        let plaintext =
311            Plaintext::<CurrentNetwork>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
312        assert!(plaintext.is_err());
313    }
314
315    #[test]
316    fn test_nested_structs1() {
317        let expected = r"{
318  r1: {
319    c1: 1u8,
320    c2: 2u8,
321    c3: 1u8
322  },
323  r2: {
324    c1: 2u8,
325    c2: 2u8,
326    c3: 1u8
327  },
328  r3: {
329    c1: 1u8,
330    c2: 2u8,
331    c3: 1u8
332  }
333}";
334
335        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
336        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
337        assert_eq!(expected, candidate.to_string());
338        assert_eq!("", remainder);
339    }
340
341    #[test]
342    fn test_nested_structs2() {
343        let expected = r"{
344  foo: {
345    bar: {
346      baz: 1u8
347    },
348    qux: {
349      quux: 2u8
350    }
351  }
352}";
353
354        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
355        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
356        assert_eq!(expected, candidate.to_string());
357        assert_eq!("", remainder);
358    }
359
360    #[test]
361    fn test_nested_structs3() {
362        let expected = r"{
363  c: {
364    a: 0u8,
365    b: 1u8
366  },
367  d: {
368    a: 0u8,
369    b: 1u8
370  }
371}";
372
373        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
374        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
375        assert_eq!(expected, candidate.to_string());
376        assert_eq!("", remainder);
377    }
378
379    #[test]
380    fn test_array() {
381        // Test an array of literals.
382        let expected = r"[
383  1u8,
384  2u8,
385  3u8
386]";
387        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
388        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
389        assert_eq!(expected, candidate.to_string());
390        assert_eq!("", remainder);
391
392        // Test an array of structs.
393        let expected = r"[
394  {
395    foo: 1u8,
396    bar: 2u8
397  },
398  {
399    foo: 3u8,
400    bar: 4u8
401  },
402  {
403    foo: 5u8,
404    bar: 6u8
405  }
406]";
407        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
408        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
409        assert_eq!(expected, candidate.to_string());
410        assert_eq!("", remainder);
411    }
412
413    #[test]
414    fn test_struct_with_arrays() {
415        let expected = r"{
416  foo: [
417    1u8,
418    2u8,
419    3u8
420  ],
421  bar: [
422    4u8,
423    5u8,
424    6u8
425  ]
426}";
427        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
428        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
429        assert_eq!(expected, candidate.to_string());
430        assert_eq!("", remainder);
431    }
432
433    #[test]
434    fn test_struct_with_array_of_structs() {
435        let expected = r"{
436  foo: [
437    {
438      foo: 1u8,
439      bar: 2u8
440    },
441    {
442      foo: 3u8,
443      bar: 4u8
444    },
445    {
446      foo: 5u8,
447      bar: 6u8
448    }
449  ],
450  bar: [
451    {
452      foo: [
453        1u8,
454        2u8,
455        3u8
456      ],
457      bar: [
458        4u8,
459        5u8,
460        6u8
461      ]
462    },
463    {
464      foo: [
465        7u8,
466        8u8,
467        9u8
468      ],
469      bar: [
470        10u8,
471        11u8,
472        12u8
473      ]
474    },
475    {
476      foo: [
477        13u8,
478        14u8,
479        15u8
480      ],
481      bar: [
482        16u8,
483        17u8,
484        18u8
485      ]
486    }
487  ]
488}";
489        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
490        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
491        assert_eq!(expected, candidate.to_string());
492        assert_eq!("", remainder);
493    }
494
495    // A helper function to get the depth of the plaintext.
496    fn get_depth(plaintext: &Plaintext<CurrentNetwork>) -> usize {
497        match plaintext {
498            Plaintext::Literal(_, _) => 0,
499            Plaintext::Struct(members, _) => members.values().map(get_depth).max().unwrap_or(0) + 1,
500            Plaintext::Array(elements, _) => elements.iter().map(get_depth).max().unwrap_or(0) + 1,
501        }
502    }
503
504    #[test]
505    fn test_deeply_nested_plaintext() {
506        // Creates a string representation of a nested Plaintext array with the given depth and root.
507        fn create_nested_array(depth: usize, root: impl Display) -> String {
508            // Define the prefix and suffix based on the depth.
509            let prefix = if depth == 0 { "".to_string() } else { "[".repeat(depth) };
510            let suffix = if depth == 0 { "".to_string() } else { "]".repeat(depth) };
511            // Format the string with the prefix, root, and suffix.
512            format!("{prefix}{root}{suffix}")
513        }
514
515        // Creates a string representation of a nested Plaintext struct with the given depth and root.
516        fn create_nested_struct(depth: usize, root: impl Display) -> String {
517            // Define the prefix and suffix based on the depth.
518            let prefix = if depth == 0 { "".to_string() } else { "{inner:".repeat(depth) };
519            let suffix = if depth == 0 { "".to_string() } else { "}".repeat(depth) };
520            // Format the string with the prefix, root, and suffix.
521            format!("{prefix}{root}{suffix}")
522        }
523
524        // Creates a string representation of a nested Plaintext object with alternating structs and arrays with the given depth and root.
525        fn create_alternated_nested(depth: usize, root: impl Display) -> String {
526            let prefix = (0..depth).map(|i| if i % 2 == 0 { "[" } else { "{inner:" }).collect::<String>();
527            let suffix = (0..depth).map(|i| if i % 2 == 0 { "]" } else { "}" }).rev().collect::<String>();
528            format!("{prefix}{root}{suffix}")
529        }
530
531        // A helper function to run the test.
532        fn run_test(expected_depth: usize, input: String, expected_error: bool) {
533            // Parse the input string.
534            let result = Plaintext::<CurrentNetwork>::parse(&input);
535            // Check if the result is an error.
536            match expected_error {
537                true => {
538                    assert!(result.is_err());
539                    return;
540                }
541                false => assert!(result.is_ok()),
542            };
543            // Unwrap the result.
544            let (remainder, candidate) = result.unwrap();
545            // Check if the remainder is empty.
546            assert!(remainder.is_empty());
547            // Check if the candidate is equal to the input, with whitespace removed.
548            assert_eq!(input, candidate.to_string().replace("\n", "").replace(" ", ""));
549            // Check if the candidate is equal to the expected depth.
550            assert_eq!(get_depth(&candidate), expected_depth);
551        }
552
553        // Initialize a sequence of depths to check.
554        let mut depths = (0usize..100).collect_vec();
555        depths.extend((100..1000).step_by(100));
556        depths.extend((1000..10000).step_by(1000));
557        depths.extend((10000..100000).step_by(10000));
558
559        // Test deeply nested arrays with different literal types.
560        for i in depths.iter().copied() {
561            run_test(i, create_nested_array(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
562            run_test(i, create_nested_array(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
563            run_test(i, create_nested_array(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
564            run_test(i, create_nested_array(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
565        }
566
567        // Test deeply nested structs with different literal types.
568        for i in depths.iter().copied() {
569            run_test(i, create_nested_struct(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
570            run_test(i, create_nested_struct(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
571            run_test(i, create_nested_struct(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
572            run_test(i, create_nested_struct(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
573        }
574
575        // Test alternating nested arrays and structs.
576        for i in depths.iter().copied() {
577            run_test(i, create_alternated_nested(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
578            run_test(i, create_alternated_nested(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
579            run_test(i, create_alternated_nested(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
580            run_test(i, create_alternated_nested(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
581        }
582    }
583}