/ console / program / src / data / plaintext / parse.rs
parse.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm 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  
 16  use super::*;
 17  
 18  impl<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  
 27  impl<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  
112  impl<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  
129  impl<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  
136  impl<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  
143  impl<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)]
223  mod tests {
224      use super::*;
225      use alphavm_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  }