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 }