parse.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm 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 ProgramCore<N> { 19 /// Parses a string into a program. 20 #[inline] 21 fn parse(string: &str) -> ParserResult<Self> { 22 // A helper to parse a program. 23 enum P<N: Network> { 24 Constructor(ConstructorCore<N>), 25 M(Mapping<N>), 26 S(StructType<N>), 27 R(RecordType<N>), 28 C(ClosureCore<N>), 29 F(FunctionCore<N>), 30 } 31 32 // Parse the imports from the string. 33 let (string, imports) = many0(Import::parse)(string)?; 34 // Parse the whitespace and comments from the string. 35 let (string, _) = Sanitizer::parse(string)?; 36 // Parse the 'program' keyword from the string. 37 let (string, _) = tag(Self::type_name())(string)?; 38 // Parse the whitespace from the string. 39 let (string, _) = Sanitizer::parse_whitespaces(string)?; 40 // Parse the program ID from the string. 41 let (string, id) = ProgramID::parse(string)?; 42 // Parse the whitespace from the string. 43 let (string, _) = Sanitizer::parse_whitespaces(string)?; 44 // Parse the semicolon ';' keyword from the string. 45 let (string, _) = tag(";")(string)?; 46 47 fn intermediate<N: Network>(string: &str) -> ParserResult<P<N>> { 48 // Parse the whitespace and comments from the string. 49 let (string, _) = Sanitizer::parse(string)?; 50 51 if string.starts_with(ConstructorCore::<N>::type_name()) { 52 map(ConstructorCore::parse, |constructor| P::<N>::Constructor(constructor))(string) 53 } else if string.starts_with(Mapping::<N>::type_name()) { 54 map(Mapping::parse, |mapping| P::<N>::M(mapping))(string) 55 } else if string.starts_with(StructType::<N>::type_name()) { 56 map(StructType::parse, |struct_| P::<N>::S(struct_))(string) 57 } else if string.starts_with(RecordType::<N>::type_name()) { 58 map(RecordType::parse, |record| P::<N>::R(record))(string) 59 } else if string.starts_with(ClosureCore::<N>::type_name()) { 60 map(ClosureCore::parse, |closure| P::<N>::C(closure))(string) 61 } else if string.starts_with(FunctionCore::<N>::type_name()) { 62 map(FunctionCore::parse, |function| P::<N>::F(function))(string) 63 } else { 64 Err(Err::Error(make_error(string, ErrorKind::Alt))) 65 } 66 } 67 68 // Parse the struct or function from the string. 69 let (string, components) = many1(intermediate)(string)?; 70 // Parse the whitespace and comments from the string. 71 let (string, _) = Sanitizer::parse(string)?; 72 73 // Initialize a new program. 74 let mut program = match ProgramCore::<N>::new(id) { 75 Ok(program) => program, 76 Err(error) => { 77 eprintln!("{error}"); 78 return map_res(take(0usize), Err)(string); 79 } 80 }; 81 // Construct the program with the parsed components. 82 for component in components { 83 let result = match component { 84 P::Constructor(constructor) => program.add_constructor(constructor), 85 P::M(mapping) => program.add_mapping(mapping), 86 P::S(struct_) => program.add_struct(struct_), 87 P::R(record) => program.add_record(record), 88 P::C(closure) => program.add_closure(closure), 89 P::F(function) => program.add_function(function), 90 }; 91 92 match result { 93 Ok(_) => (), 94 Err(error) => { 95 eprintln!("{error}"); 96 return map_res(take(0usize), Err)(string); 97 } 98 } 99 } 100 // Lastly, add the imports (if any) to the program. 101 for import in imports { 102 match program.add_import(import) { 103 Ok(_) => (), 104 Err(error) => { 105 eprintln!("{error}"); 106 return map_res(take(0usize), Err)(string); 107 } 108 } 109 } 110 111 Ok((string, program)) 112 } 113 } 114 115 impl<N: Network> FromStr for ProgramCore<N> { 116 type Err = Error; 117 118 /// Returns a program from a string literal. 119 fn from_str(string: &str) -> Result<Self> { 120 // Ensure the raw program string is less than MAX_PROGRAM_SIZE. 121 ensure!(string.len() <= N::MAX_PROGRAM_SIZE, "Program length exceeds N::MAX_PROGRAM_SIZE."); 122 123 match Self::parse(string) { 124 Ok((remainder, object)) => { 125 // Ensure the remainder is empty. 126 ensure!(remainder.is_empty(), "Failed to parse string. Remaining invalid string is: \"{remainder}\""); 127 // Return the object. 128 Ok(object) 129 } 130 Err(error) => bail!("Failed to parse string. {error}"), 131 } 132 } 133 } 134 135 impl<N: Network> Debug for ProgramCore<N> { 136 /// Prints the program as a string. 137 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 138 Display::fmt(self, f) 139 } 140 } 141 142 impl<N: Network> Display for ProgramCore<N> { 143 /// Prints the program as a string. 144 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 145 if !self.imports.is_empty() { 146 // Print the imports. 147 for import in self.imports.values() { 148 writeln!(f, "{import}")?; 149 } 150 151 // Print a newline. 152 writeln!(f)?; 153 } 154 155 // Print the program name. 156 write!(f, "{} {};\n\n", Self::type_name(), self.id)?; 157 158 // Write the components. 159 let mut components_iter = self.components.iter().peekable(); 160 while let Some((label, definition)) = components_iter.next() { 161 match label { 162 ProgramLabel::Constructor => { 163 // Write the constructor, if it exists. 164 if let Some(constructor) = &self.constructor { 165 writeln!(f, "{constructor}")?; 166 } 167 } 168 ProgramLabel::Identifier(identifier) => match definition { 169 ProgramDefinition::Constructor => return Err(fmt::Error), 170 ProgramDefinition::Mapping => match self.mappings.get(identifier) { 171 Some(mapping) => writeln!(f, "{mapping}")?, 172 None => return Err(fmt::Error), 173 }, 174 ProgramDefinition::Struct => match self.structs.get(identifier) { 175 Some(struct_) => writeln!(f, "{struct_}")?, 176 None => return Err(fmt::Error), 177 }, 178 ProgramDefinition::Record => match self.records.get(identifier) { 179 Some(record) => writeln!(f, "{record}")?, 180 None => return Err(fmt::Error), 181 }, 182 ProgramDefinition::Closure => match self.closures.get(identifier) { 183 Some(closure) => writeln!(f, "{closure}")?, 184 None => return Err(fmt::Error), 185 }, 186 ProgramDefinition::Function => match self.functions.get(identifier) { 187 Some(function) => writeln!(f, "{function}")?, 188 None => return Err(fmt::Error), 189 }, 190 }, 191 } 192 193 // Omit the last newline. 194 if components_iter.peek().is_some() { 195 writeln!(f)?; 196 } 197 } 198 199 Ok(()) 200 } 201 } 202 203 #[cfg(test)] 204 mod tests { 205 use super::*; 206 use crate::Program; 207 use console::network::MainnetV0; 208 209 type CurrentNetwork = MainnetV0; 210 211 #[test] 212 fn test_program_parse() -> Result<()> { 213 // Initialize a new program. 214 let (string, program) = Program::<CurrentNetwork>::parse( 215 r" 216 program to_parse.delta; 217 218 struct message: 219 first as field; 220 second as field; 221 222 function compute: 223 input r0 as message.private; 224 add r0.first r0.second into r1; 225 output r1 as field.private;", 226 ) 227 .unwrap(); 228 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'"); 229 230 // Ensure the program contains the struct. 231 assert!(program.contains_struct(&Identifier::from_str("message")?)); 232 // Ensure the program contains the function. 233 assert!(program.contains_function(&Identifier::from_str("compute")?)); 234 235 Ok(()) 236 } 237 238 #[test] 239 fn test_program_parse_function_zero_inputs() -> Result<()> { 240 // Initialize a new program. 241 let (string, program) = Program::<CurrentNetwork>::parse( 242 r" 243 program to_parse.delta; 244 245 function compute: 246 add 1u32 2u32 into r0; 247 output r0 as u32.private;", 248 ) 249 .unwrap(); 250 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'"); 251 252 // Ensure the program contains the function. 253 assert!(program.contains_function(&Identifier::from_str("compute")?)); 254 255 Ok(()) 256 } 257 258 #[test] 259 fn test_program_display() -> Result<()> { 260 let expected = r"program to_parse.delta; 261 262 struct message: 263 first as field; 264 second as field; 265 266 function compute: 267 input r0 as message.private; 268 add r0.first r0.second into r1; 269 output r1 as field.private; 270 "; 271 // Parse a new program. 272 let program = Program::<CurrentNetwork>::from_str(expected)?; 273 // Ensure the program string matches. 274 assert_eq!(expected, format!("{program}")); 275 276 Ok(()) 277 } 278 279 #[test] 280 fn test_program_size() { 281 // Define variable name for easy experimentation with program sizes. 282 let var_name = "a"; 283 284 // Helper function to generate imports. 285 let gen_import_string = |n: usize| -> String { 286 let mut s = String::new(); 287 for i in 0..n { 288 s.push_str(&format!("import foo{i}.delta;\n")); 289 } 290 s 291 }; 292 293 // Helper function to generate large structs. 294 let gen_struct_string = |n: usize| -> String { 295 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE); 296 for i in 0..n { 297 s.push_str(&format!("struct m{i}:\n")); 298 for j in 0..10 { 299 s.push_str(&format!(" {var_name}{j} as u128;\n")); 300 } 301 } 302 s 303 }; 304 305 // Helper function to generate large records. 306 let gen_record_string = |n: usize| -> String { 307 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE); 308 for i in 0..n { 309 s.push_str(&format!("record r{i}:\n owner as address.private;\n")); 310 for j in 0..10 { 311 s.push_str(&format!(" {var_name}{j} as u128.private;\n")); 312 } 313 } 314 s 315 }; 316 317 // Helper function to generate large mappings. 318 let gen_mapping_string = |n: usize| -> String { 319 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE); 320 for i in 0..n { 321 s.push_str(&format!("mapping {var_name}{i}:\n key as field.public;\n value as field.public;\n")); 322 } 323 s 324 }; 325 326 // Helper function to generate large closures. 327 let gen_closure_string = |n: usize| -> String { 328 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE); 329 for i in 0..n { 330 s.push_str(&format!("closure c{i}:\n input r0 as u128;\n")); 331 for j in 0..10 { 332 s.push_str(&format!(" add r0 r0 into r{j};\n")); 333 } 334 s.push_str(&format!(" output r{} as u128;\n", 4000)); 335 } 336 s 337 }; 338 339 // Helper function to generate large functions. 340 let gen_function_string = |n: usize| -> String { 341 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE); 342 for i in 0..n { 343 s.push_str(&format!("function f{i}:\n add 1u128 1u128 into r0;\n")); 344 for j in 0..10 { 345 s.push_str(&format!(" add r0 r0 into r{j};\n")); 346 } 347 } 348 s 349 }; 350 351 // Helper function to generate and parse a program. 352 let test_parse = |imports: &str, body: &str, should_succeed: bool| { 353 let program = format!("{imports}\nprogram to_parse.delta;\n\n{body}"); 354 let result = Program::<CurrentNetwork>::from_str(&program); 355 if result.is_ok() != should_succeed { 356 println!("Program failed to parse: {program}"); 357 } 358 assert_eq!(result.is_ok(), should_succeed); 359 }; 360 361 // A program with MAX_IMPORTS should succeed. 362 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS), &gen_struct_string(1), true); 363 // A program with more than MAX_IMPORTS should fail. 364 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS + 1), &gen_struct_string(1), false); 365 // A program with MAX_STRUCTS should succeed. 366 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS), true); 367 // A program with more than MAX_STRUCTS should fail. 368 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS + 1), false); 369 // A program with MAX_RECORDS should succeed. 370 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS), true); 371 // A program with more than MAX_RECORDS should fail. 372 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS + 1), false); 373 // A program with MAX_MAPPINGS should succeed. 374 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS), true); 375 // A program with more than MAX_MAPPINGS should fail. 376 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS + 1), false); 377 // A program with MAX_CLOSURES should succeed. 378 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES), true); 379 // A program with more than MAX_CLOSURES should fail. 380 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES + 1), false); 381 // A program with MAX_FUNCTIONS should succeed. 382 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS), true); 383 // A program with more than MAX_FUNCTIONS should fail. 384 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS + 1), false); 385 386 // Initialize a program which is too big. 387 let program_too_big = format!( 388 "{} {} {} {} {}", 389 gen_struct_string(CurrentNetwork::MAX_STRUCTS), 390 gen_record_string(CurrentNetwork::MAX_RECORDS), 391 gen_mapping_string(CurrentNetwork::MAX_MAPPINGS), 392 gen_closure_string(CurrentNetwork::MAX_CLOSURES), 393 gen_function_string(CurrentNetwork::MAX_FUNCTIONS) 394 ); 395 // A program which is too big should fail. 396 test_parse("", &program_too_big, false); 397 } 398 }