parse.rs
1 // Copyright (c) 2025-2026 ACDC Network 2 // This file is part of the alphavm library. 3 // 4 // Alpha Chain | Delta Chain Protocol 5 // International Monetary Graphite. 6 // 7 // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com). 8 // They built world-class ZK infrastructure. We installed the EASY button. 9 // Their cryptography: elegant. Our modifications: bureaucracy-compatible. 10 // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours. 11 // 12 // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0 13 // All modifications and new work: CC0 1.0 Universal Public Domain Dedication. 14 // No rights reserved. No permission required. No warranty. No refunds. 15 // 16 // https://creativecommons.org/publicdomain/zero/1.0/ 17 // SPDX-License-Identifier: CC0-1.0 18 19 use super::*; 20 21 impl<N: Network> Parser for StructType<N> { 22 /// Parses a struct as: 23 /// ```text 24 /// struct message: 25 /// owner as address; 26 /// amount as u64; 27 /// ``` 28 #[inline] 29 fn parse(string: &str) -> ParserResult<'_, Self> { 30 /// Parses a string into a tuple. 31 fn parse_tuple<N: Network>(string: &str) -> ParserResult<'_, (Identifier<N>, PlaintextType<N>)> { 32 // Parse the whitespace and comments from the string. 33 let (string, _) = Sanitizer::parse(string)?; 34 // Parse the identifier from the string. 35 let (string, identifier) = Identifier::parse(string)?; 36 // Parse the whitespace from the string. 37 let (string, _) = Sanitizer::parse_whitespaces(string)?; 38 // Parse the "as" from the string. 39 let (string, _) = tag("as")(string)?; 40 // Parse the whitespace from the string. 41 let (string, _) = Sanitizer::parse_whitespaces(string)?; 42 // Parse the plaintext type from the string. 43 let (string, plaintext_type) = PlaintextType::parse(string)?; 44 // Parse the whitespace from the string. 45 let (string, _) = Sanitizer::parse_whitespaces(string)?; 46 // Parse the semicolon ';' keyword from the string. 47 let (string, _) = tag(";")(string)?; 48 // Return the identifier and plaintext type. 49 Ok((string, (identifier, plaintext_type))) 50 } 51 52 // Parse the whitespace and comments from the string. 53 let (string, _) = Sanitizer::parse(string)?; 54 // Parse the type name from the string. 55 let (string, _) = tag(Self::type_name())(string)?; 56 // Parse the whitespace from the string. 57 let (string, _) = Sanitizer::parse_whitespaces(string)?; 58 // Parse the struct name from the string. 59 let (string, name) = Identifier::parse(string)?; 60 // Parse the whitespace from the string. 61 let (string, _) = Sanitizer::parse_whitespaces(string)?; 62 // Parse the colon ':' keyword from the string. 63 let (string, _) = tag(":")(string)?; 64 // Parse the members from the string. 65 let (string, members) = map_res(many1(parse_tuple), |members| { 66 // Ensure the members has no duplicate names. 67 if has_duplicates(members.iter().map(|(identifier, _)| identifier)) { 68 return Err(error(format!("Duplicate identifier found in struct '{name}'"))); 69 } 70 // Ensure the number of members is within the maximum limit. 71 if members.len() > N::MAX_STRUCT_ENTRIES { 72 return Err(error("Failed to parse struct: too many members")); 73 } 74 Ok(members) 75 })(string)?; 76 // Return the struct. 77 Ok((string, Self { name, members: IndexMap::from_iter(members) })) 78 } 79 } 80 81 impl<N: Network> FromStr for StructType<N> { 82 type Err = Error; 83 84 /// Returns a struct from a string literal. 85 fn from_str(string: &str) -> Result<Self> { 86 match Self::parse(string) { 87 Ok((remainder, object)) => { 88 // Ensure the remainder is empty. 89 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\""); 90 // Return the object. 91 Ok(object) 92 } 93 Err(error) => bail!("Failed to parse string. {error}"), 94 } 95 } 96 } 97 98 impl<N: Network> Debug for StructType<N> { 99 /// Prints the struct type as a string. 100 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 101 Display::fmt(self, f) 102 } 103 } 104 105 #[allow(clippy::format_push_string)] 106 impl<N: Network> Display for StructType<N> { 107 /// Prints the struct type as a string. 108 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 109 let mut output = format!("{} {}:\n", Self::type_name(), self.name); 110 for (identifier, plaintext_type) in &self.members { 111 output += &format!(" {identifier} as {plaintext_type};\n"); 112 } 113 output.pop(); // trailing newline 114 write!(f, "{output}") 115 } 116 } 117 118 #[cfg(test)] 119 mod tests { 120 use super::*; 121 use alphavm_console_network::MainnetV0; 122 123 type CurrentNetwork = MainnetV0; 124 125 #[test] 126 fn test_parse() -> Result<()> { 127 let expected = StructType::<CurrentNetwork> { 128 name: Identifier::from_str("message")?, 129 members: IndexMap::from_iter(vec![ 130 (Identifier::from_str("sender")?, PlaintextType::from_str("address")?), 131 (Identifier::from_str("amount")?, PlaintextType::from_str("u64")?), 132 ]), 133 }; 134 135 let (remainder, candidate) = StructType::<CurrentNetwork>::parse( 136 r" 137 struct message: 138 sender as address; 139 amount as u64; 140 ", 141 )?; 142 assert_eq!("\n", remainder); 143 assert_eq!(expected, candidate); 144 Ok(()) 145 } 146 147 #[test] 148 fn test_parse_fails() { 149 // Must be non-empty. 150 assert!(StructType::<CurrentNetwork>::parse("").is_err()); 151 assert!(StructType::<CurrentNetwork>::parse("struct message:").is_err()); 152 153 // Invalid characters. 154 assert!(StructType::<CurrentNetwork>::parse("{}").is_err()); 155 assert!(StructType::<CurrentNetwork>::parse("_").is_err()); 156 assert!(StructType::<CurrentNetwork>::parse("__").is_err()); 157 assert!(StructType::<CurrentNetwork>::parse("___").is_err()); 158 assert!(StructType::<CurrentNetwork>::parse("-").is_err()); 159 assert!(StructType::<CurrentNetwork>::parse("--").is_err()); 160 assert!(StructType::<CurrentNetwork>::parse("---").is_err()); 161 assert!(StructType::<CurrentNetwork>::parse("*").is_err()); 162 assert!(StructType::<CurrentNetwork>::parse("**").is_err()); 163 assert!(StructType::<CurrentNetwork>::parse("***").is_err()); 164 165 // Must not start with a number. 166 assert!(StructType::<CurrentNetwork>::parse("1").is_err()); 167 assert!(StructType::<CurrentNetwork>::parse("2").is_err()); 168 assert!(StructType::<CurrentNetwork>::parse("3").is_err()); 169 assert!(StructType::<CurrentNetwork>::parse("1foo").is_err()); 170 assert!(StructType::<CurrentNetwork>::parse("12").is_err()); 171 assert!(StructType::<CurrentNetwork>::parse("111").is_err()); 172 173 // Must fit within the data capacity of a base field element. 174 let struct_ = 175 StructType::<CurrentNetwork>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy"); 176 assert!(struct_.is_err()); 177 } 178 179 #[test] 180 fn test_display() { 181 let expected = "struct message:\n first as field;\n second as field;"; 182 let message = StructType::<CurrentNetwork>::parse(expected).unwrap().1; 183 assert_eq!(expected, format!("{message}")); 184 } 185 186 #[test] 187 fn test_display_fails() { 188 // Duplicate identifier. 189 let candidate = 190 StructType::<CurrentNetwork>::parse("struct message:\n first as field;\n first as field;"); 191 assert!(candidate.is_err()); 192 // Visibility in plaintext type. 193 let candidate = StructType::<CurrentNetwork>::parse( 194 "struct message:\n first as field.public;\n first as field.private;", 195 ); 196 assert!(candidate.is_err()); 197 } 198 199 #[test] 200 fn test_max_members() { 201 let mut string = "struct message:\n".to_string(); 202 for i in 0..CurrentNetwork::MAX_STRUCT_ENTRIES { 203 string += &format!(" member_{i} as field;\n"); 204 } 205 assert!(StructType::<CurrentNetwork>::parse(&string).is_ok()); 206 } 207 208 #[test] 209 fn test_too_many_members() { 210 let mut string = "struct message:\n".to_string(); 211 for i in 0..=CurrentNetwork::MAX_STRUCT_ENTRIES { 212 string += &format!(" member_{i} as field;\n"); 213 } 214 assert!(StructType::<CurrentNetwork>::parse(&string).is_err()); 215 } 216 }