snarkvm_console_program/data/plaintext/
parse.rs

1// Copyright 2024 Aleo Network Foundation
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        /// Parses a sanitized pair: `identifier: plaintext`.
23        fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Plaintext<N>)> {
24            // Parse the whitespace and comments from the string.
25            let (string, _) = Sanitizer::parse(string)?;
26            // Parse the identifier from the string.
27            let (string, identifier) = Identifier::parse(string)?;
28            // Parse the whitespace from the string.
29            let (string, _) = Sanitizer::parse_whitespaces(string)?;
30            // Parse the ":" from the string.
31            let (string, _) = tag(":")(string)?;
32            // Parse the plaintext from the string.
33            let (string, plaintext) = Plaintext::parse(string)?;
34            // Parse the whitespace from the string.
35            let (string, _) = Sanitizer::parse_whitespaces(string)?;
36            // Return the identifier and plaintext.
37            Ok((string, (identifier, plaintext)))
38        }
39
40        /// Parses a plaintext as a struct: `{ identifier_0: plaintext_0, ..., identifier_n: plaintext_n }`.
41        fn parse_struct<N: Network>(string: &str) -> ParserResult<Plaintext<N>> {
42            // Parse the whitespace and comments from the string.
43            let (string, _) = Sanitizer::parse(string)?;
44            // Parse the "{" from the string.
45            let (string, _) = tag("{")(string)?;
46            // Parse the members.
47            let (string, members) = map_res(separated_list1(tag(","), parse_pair), |members: Vec<_>| {
48                // Ensure the members has no duplicate names.
49                if has_duplicates(members.iter().map(|(name, ..)| name)) {
50                    return Err(error("Duplicate member in struct"));
51                }
52                // Ensure the number of structs is within the maximum limit.
53                match members.len() <= N::MAX_STRUCT_ENTRIES {
54                    true => Ok(members),
55                    false => Err(error(format!("Found a plaintext that exceeds size ({})", members.len()))),
56                }
57            })(string)?;
58            // Parse the whitespace and comments from the string.
59            let (string, _) = Sanitizer::parse(string)?;
60            // Parse the '}' from the string.
61            let (string, _) = tag("}")(string)?;
62            // Output the plaintext.
63            Ok((string, Plaintext::Struct(IndexMap::from_iter(members), Default::default())))
64        }
65
66        /// Parses a plaintext as an array: `[plaintext_0, ..., plaintext_n]`.
67        fn parse_array<N: Network>(string: &str) -> ParserResult<Plaintext<N>> {
68            // Parse the whitespace and comments from the string.
69            let (string, _) = Sanitizer::parse(string)?;
70            // Parse the "[" from the string.
71            let (string, _) = tag("[")(string)?;
72            // Parse the members.
73            let (string, members) = separated_list1(tag(","), Plaintext::parse)(string)?;
74            // Parse the whitespace and comments from the string.
75            let (string, _) = Sanitizer::parse(string)?;
76            // Parse the ']' from the string.
77            let (string, _) = tag("]")(string)?;
78            // Output the plaintext.
79            Ok((string, Plaintext::Array(members, Default::default())))
80        }
81
82        // Parse the whitespace from the string.
83        let (string, _) = Sanitizer::parse_whitespaces(string)?;
84        // Parse to determine the plaintext (order matters).
85        alt((
86            // Parse a plaintext literal.
87            map(Literal::parse, |literal| Self::Literal(literal, Default::default())),
88            // Parse a plaintext struct.
89            parse_struct,
90            // Parse a plaintext array.
91            parse_array,
92        ))(string)
93    }
94}
95
96impl<N: Network> FromStr for Plaintext<N> {
97    type Err = Error;
98
99    /// Returns a plaintext from a string literal.
100    fn from_str(string: &str) -> Result<Self> {
101        match Self::parse(string) {
102            Ok((remainder, object)) => {
103                // Ensure the remainder is empty.
104                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
105                // Return the object.
106                Ok(object)
107            }
108            Err(error) => bail!("Failed to parse string. {error}"),
109        }
110    }
111}
112
113impl<N: Network> Debug for Plaintext<N> {
114    /// Prints the plaintext as a string.
115    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
116        Display::fmt(self, f)
117    }
118}
119
120impl<N: Network> Display for Plaintext<N> {
121    /// Prints the plaintext as a string.
122    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
123        self.fmt_internal(f, 0)
124    }
125}
126
127impl<N: Network> Plaintext<N> {
128    /// Prints the plaintext with the given indentation depth.
129    fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
130        /// The number of spaces to indent.
131        const INDENT: usize = 2;
132
133        match self {
134            // Prints the literal, i.e. 10field
135            Self::Literal(literal, ..) => write!(f, "{:indent$}{literal}", "", indent = depth * INDENT),
136            // Prints the struct, i.e. { first: 10i64, second: 198u64 }
137            Self::Struct(struct_, ..) => {
138                // Print the opening brace.
139                write!(f, "{{")?;
140                // Print the members.
141                struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
142                    match plaintext {
143                        Self::Literal(literal, ..) => match i == struct_.len() - 1 {
144                            true => {
145                                // Print the last member without a comma.
146                                write!(f, "\n{:indent$}{name}: {literal}", "", indent = (depth + 1) * INDENT)?;
147                                // Print the closing brace.
148                                write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
149                            }
150                            // Print the member with a comma.
151                            false => write!(f, "\n{:indent$}{name}: {literal},", "", indent = (depth + 1) * INDENT),
152                        },
153                        Self::Struct(..) | Self::Array(..) => {
154                            // Print the member name.
155                            write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
156                            // Print the member.
157                            plaintext.fmt_internal(f, depth + 1)?;
158                            // Print the closing brace.
159                            match i == struct_.len() - 1 {
160                                // Print the last member without a comma.
161                                true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
162                                // Print the member with a comma.
163                                false => write!(f, ","),
164                            }
165                        }
166                    }
167                })
168            }
169            // Prints the array, i.e. [ 10u64, 198u64 ]
170            Self::Array(array, ..) => {
171                // Print the opening bracket.
172                write!(f, "[")?;
173                // Print the members.
174                array.iter().enumerate().try_for_each(|(i, plaintext)| {
175                    match plaintext {
176                        Self::Literal(literal, ..) => match i == array.len() - 1 {
177                            true => {
178                                // Print the last member without a comma.
179                                write!(f, "\n{:indent$}{literal}", "", indent = (depth + 1) * INDENT)?;
180                                // Print the closing bracket.
181                                write!(f, "\n{:indent$}]", "", indent = depth * INDENT)
182                            }
183                            // Print the member with a comma.
184                            false => write!(f, "\n{:indent$}{literal},", "", indent = (depth + 1) * INDENT),
185                        },
186                        Self::Struct(..) | Self::Array(..) => {
187                            // Print a newline.
188                            write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT)?;
189                            // Print the member.
190                            plaintext.fmt_internal(f, depth + 1)?;
191                            // Print the closing brace.
192                            match i == array.len() - 1 {
193                                // Print the last member without a comma.
194                                true => write!(f, "\n{:indent$}]", "", indent = depth * INDENT),
195                                // Print the member with a comma.
196                                false => write!(f, ","),
197                            }
198                        }
199                    }
200                })
201            }
202        }
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use snarkvm_console_network::MainnetV0;
210
211    type CurrentNetwork = MainnetV0;
212
213    #[test]
214    fn test_parse_literal() -> Result<()> {
215        // Sanity check.
216        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("5u8")?;
217        assert_eq!("5u8", candidate.to_string());
218        assert_eq!("", remainder);
219
220        Ok(())
221    }
222
223    #[test]
224    fn test_parse_struct() -> Result<()> {
225        // Sanity check.
226        let expected = r"{
227  foo: 5u8
228}";
229        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("{ foo: 5u8 }")?;
230        assert_eq!(expected, candidate.to_string());
231        assert_eq!("", remainder);
232
233        let expected = r"{
234  foo: 5u8,
235  bar: {
236    baz: 10field,
237    qux: {
238      quux: {
239        corge: {
240          grault: {
241            garply: {
242              waldo: {
243                fred: {
244                  plugh: {
245                    xyzzy: {
246                      thud: true
247                    }
248                  }
249                }
250              }
251            }
252          }
253        }
254      }
255    }
256  }
257}";
258        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(
259            "{ foo: 5u8, bar: { baz: 10field, qux: {quux:{corge :{grault:  {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true}} }}}  }}}}}}",
260        )?;
261        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
262        assert_eq!(expected, candidate.to_string());
263        assert_eq!("", remainder);
264
265        Ok(())
266    }
267
268    #[test]
269    fn test_parse_fails() {
270        // Must be non-empty.
271        assert!(Plaintext::<CurrentNetwork>::parse("").is_err());
272        assert!(Plaintext::<CurrentNetwork>::parse("{}").is_err());
273
274        // Invalid characters.
275        assert!(Plaintext::<CurrentNetwork>::parse("_").is_err());
276        assert!(Plaintext::<CurrentNetwork>::parse("__").is_err());
277        assert!(Plaintext::<CurrentNetwork>::parse("___").is_err());
278        assert!(Plaintext::<CurrentNetwork>::parse("-").is_err());
279        assert!(Plaintext::<CurrentNetwork>::parse("--").is_err());
280        assert!(Plaintext::<CurrentNetwork>::parse("---").is_err());
281        assert!(Plaintext::<CurrentNetwork>::parse("*").is_err());
282        assert!(Plaintext::<CurrentNetwork>::parse("**").is_err());
283        assert!(Plaintext::<CurrentNetwork>::parse("***").is_err());
284
285        // Must not start with a number.
286        assert!(Plaintext::<CurrentNetwork>::parse("1").is_err());
287        assert!(Plaintext::<CurrentNetwork>::parse("2").is_err());
288        assert!(Plaintext::<CurrentNetwork>::parse("3").is_err());
289        assert!(Plaintext::<CurrentNetwork>::parse("1foo").is_err());
290        assert!(Plaintext::<CurrentNetwork>::parse("12").is_err());
291        assert!(Plaintext::<CurrentNetwork>::parse("111").is_err());
292
293        // Must fit within the data capacity of a base field element.
294        let plaintext =
295            Plaintext::<CurrentNetwork>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
296        assert!(plaintext.is_err());
297    }
298
299    #[test]
300    fn test_nested_structs1() {
301        let expected = r"{
302  r1: {
303    c1: 1u8,
304    c2: 2u8,
305    c3: 1u8
306  },
307  r2: {
308    c1: 2u8,
309    c2: 2u8,
310    c3: 1u8
311  },
312  r3: {
313    c1: 1u8,
314    c2: 2u8,
315    c3: 1u8
316  }
317}";
318
319        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
320        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
321        assert_eq!(expected, candidate.to_string());
322        assert_eq!("", remainder);
323    }
324
325    #[test]
326    fn test_nested_structs2() {
327        let expected = r"{
328  foo: {
329    bar: {
330      baz: 1u8
331    },
332    qux: {
333      quux: 2u8
334    }
335  }
336}";
337
338        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
339        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
340        assert_eq!(expected, candidate.to_string());
341        assert_eq!("", remainder);
342    }
343
344    #[test]
345    fn test_nested_structs3() {
346        let expected = r"{
347  c: {
348    a: 0u8,
349    b: 1u8
350  },
351  d: {
352    a: 0u8,
353    b: 1u8
354  }
355}";
356
357        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
358        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
359        assert_eq!(expected, candidate.to_string());
360        assert_eq!("", remainder);
361    }
362
363    #[test]
364    fn test_array() {
365        // Test an array of literals.
366        let expected = r"[
367  1u8,
368  2u8,
369  3u8
370]";
371        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
372        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
373        assert_eq!(expected, candidate.to_string());
374        assert_eq!("", remainder);
375
376        // Test an array of structs.
377        let expected = r"[
378  {
379    foo: 1u8,
380    bar: 2u8
381  },
382  {
383    foo: 3u8,
384    bar: 4u8
385  },
386  {
387    foo: 5u8,
388    bar: 6u8
389  }
390]";
391        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
392        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
393        assert_eq!(expected, candidate.to_string());
394        assert_eq!("", remainder);
395    }
396
397    #[test]
398    fn test_struct_with_arrays() {
399        let expected = r"{
400  foo: [
401    1u8,
402    2u8,
403    3u8
404  ],
405  bar: [
406    4u8,
407    5u8,
408    6u8
409  ]
410}";
411        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
412        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
413        assert_eq!(expected, candidate.to_string());
414        assert_eq!("", remainder);
415    }
416
417    #[test]
418    fn test_struct_with_array_of_structs() {
419        let expected = r"{
420  foo: [
421    {
422      foo: 1u8,
423      bar: 2u8
424    },
425    {
426      foo: 3u8,
427      bar: 4u8
428    },
429    {
430      foo: 5u8,
431      bar: 6u8
432    }
433  ],
434  bar: [
435    {
436      foo: [
437        1u8,
438        2u8,
439        3u8
440      ],
441      bar: [
442        4u8,
443        5u8,
444        6u8
445      ]
446    },
447    {
448      foo: [
449        7u8,
450        8u8,
451        9u8
452      ],
453      bar: [
454        10u8,
455        11u8,
456        12u8
457      ]
458    },
459    {
460      foo: [
461        13u8,
462        14u8,
463        15u8
464      ],
465      bar: [
466        16u8,
467        17u8,
468        18u8
469      ]
470    }
471  ]
472}";
473        let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
474        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
475        assert_eq!(expected, candidate.to_string());
476        assert_eq!("", remainder);
477    }
478}