snarkvm_synthesizer_program/
parse.rs1use super::*;
17
18impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Parser
19 for ProgramCore<N, Instruction, Command>
20{
21 #[inline]
23 fn parse(string: &str) -> ParserResult<Self> {
24 enum P<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> {
26 M(Mapping<N>),
27 I(StructType<N>),
28 R(RecordType<N>),
29 C(ClosureCore<N, Instruction>),
30 F(FunctionCore<N, Instruction, Command>),
31 }
32
33 let (string, imports) = many0(Import::parse)(string)?;
35 let (string, _) = Sanitizer::parse(string)?;
37 let (string, _) = tag(Self::type_name())(string)?;
39 let (string, _) = Sanitizer::parse_whitespaces(string)?;
41 let (string, id) = ProgramID::parse(string)?;
43 let (string, _) = Sanitizer::parse_whitespaces(string)?;
45 let (string, _) = tag(";")(string)?;
47
48 fn intermediate<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>>(
49 string: &str,
50 ) -> ParserResult<P<N, Instruction, Command>> {
51 let (string, _) = Sanitizer::parse(string)?;
53
54 if string.starts_with(Mapping::<N>::type_name()) {
55 map(Mapping::parse, |mapping| P::<N, Instruction, Command>::M(mapping))(string)
56 } else if string.starts_with(StructType::<N>::type_name()) {
57 map(StructType::parse, |struct_| P::<N, Instruction, Command>::I(struct_))(string)
58 } else if string.starts_with(RecordType::<N>::type_name()) {
59 map(RecordType::parse, |record| P::<N, Instruction, Command>::R(record))(string)
60 } else if string.starts_with(ClosureCore::<N, Instruction>::type_name()) {
61 map(ClosureCore::parse, |closure| P::<N, Instruction, Command>::C(closure))(string)
62 } else if string.starts_with(FunctionCore::<N, Instruction, Command>::type_name()) {
63 map(FunctionCore::parse, |function| P::<N, Instruction, Command>::F(function))(string)
64 } else {
65 Err(Err::Error(make_error(string, ErrorKind::Alt)))
66 }
67 }
68
69 let (string, components) = many1(intermediate)(string)?;
71 let (string, _) = Sanitizer::parse(string)?;
73
74 let mut program = match ProgramCore::<N, Instruction, Command>::new(id) {
76 Ok(program) => program,
77 Err(error) => {
78 eprintln!("{error}");
79 return map_res(take(0usize), Err)(string);
80 }
81 };
82 for component in components {
84 let result = match component {
85 P::M(mapping) => program.add_mapping(mapping),
86 P::I(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 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
115impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> FromStr
116 for ProgramCore<N, Instruction, Command>
117{
118 type Err = Error;
119
120 fn from_str(string: &str) -> Result<Self> {
122 ensure!(string.len() <= N::MAX_PROGRAM_SIZE, "Program length exceeds N::MAX_PROGRAM_SIZE.");
124
125 match Self::parse(string) {
126 Ok((remainder, object)) => {
127 ensure!(remainder.is_empty(), "Failed to parse string. Remaining invalid string is: \"{remainder}\"");
129 Ok(object)
131 }
132 Err(error) => bail!("Failed to parse string. {error}"),
133 }
134 }
135}
136
137impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Debug
138 for ProgramCore<N, Instruction, Command>
139{
140 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
142 Display::fmt(self, f)
143 }
144}
145
146impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> Display
147 for ProgramCore<N, Instruction, Command>
148{
149 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151 if !self.imports.is_empty() {
152 for import in self.imports.values() {
154 writeln!(f, "{import}")?;
155 }
156
157 writeln!(f)?;
159 }
160
161 write!(f, "{} {};\n\n", Self::type_name(), self.id)?;
163
164 let mut identifier_iter = self.identifiers.iter().peekable();
165 while let Some((identifier, definition)) = identifier_iter.next() {
166 match definition {
167 ProgramDefinition::Mapping => match self.mappings.get(identifier) {
168 Some(mapping) => writeln!(f, "{mapping}")?,
169 None => return Err(fmt::Error),
170 },
171 ProgramDefinition::Struct => match self.structs.get(identifier) {
172 Some(struct_) => writeln!(f, "{struct_}")?,
173 None => return Err(fmt::Error),
174 },
175 ProgramDefinition::Record => match self.records.get(identifier) {
176 Some(record) => writeln!(f, "{record}")?,
177 None => return Err(fmt::Error),
178 },
179 ProgramDefinition::Closure => match self.closures.get(identifier) {
180 Some(closure) => writeln!(f, "{closure}")?,
181 None => return Err(fmt::Error),
182 },
183 ProgramDefinition::Function => match self.functions.get(identifier) {
184 Some(function) => writeln!(f, "{function}")?,
185 None => return Err(fmt::Error),
186 },
187 }
188 if identifier_iter.peek().is_some() {
190 writeln!(f)?;
191 }
192 }
193
194 Ok(())
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use crate::Program;
202 use console::network::MainnetV0;
203
204 type CurrentNetwork = MainnetV0;
205
206 #[test]
207 fn test_program_parse() -> Result<()> {
208 let (string, program) = Program::<CurrentNetwork>::parse(
210 r"
211program to_parse.aleo;
212
213struct message:
214 first as field;
215 second as field;
216
217function compute:
218 input r0 as message.private;
219 add r0.first r0.second into r1;
220 output r1 as field.private;",
221 )
222 .unwrap();
223 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
224
225 assert!(program.contains_struct(&Identifier::from_str("message")?));
227 assert!(program.contains_function(&Identifier::from_str("compute")?));
229
230 Ok(())
231 }
232
233 #[test]
234 fn test_program_parse_function_zero_inputs() -> Result<()> {
235 let (string, program) = Program::<CurrentNetwork>::parse(
237 r"
238program to_parse.aleo;
239
240function compute:
241 add 1u32 2u32 into r0;
242 output r0 as u32.private;",
243 )
244 .unwrap();
245 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
246
247 assert!(program.contains_function(&Identifier::from_str("compute")?));
249
250 Ok(())
251 }
252
253 #[test]
254 fn test_program_display() -> Result<()> {
255 let expected = r"program to_parse.aleo;
256
257struct message:
258 first as field;
259 second as field;
260
261function compute:
262 input r0 as message.private;
263 add r0.first r0.second into r1;
264 output r1 as field.private;
265";
266 let program = Program::<CurrentNetwork>::from_str(expected)?;
268 assert_eq!(expected, format!("{program}"));
270
271 Ok(())
272 }
273
274 #[test]
275 fn test_program_size() {
276 let var_name = "a";
278
279 let gen_import_string = |n: usize| -> String {
281 let mut s = String::new();
282 for i in 0..n {
283 s.push_str(&format!("import foo{i}.aleo;\n"));
284 }
285 s
286 };
287
288 let gen_struct_string = |n: usize| -> String {
290 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
291 for i in 0..n {
292 s.push_str(&format!("struct m{}:\n", i));
293 for j in 0..10 {
294 s.push_str(&format!(" {}{} as u128;\n", var_name, j));
295 }
296 }
297 s
298 };
299
300 let gen_record_string = |n: usize| -> String {
302 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
303 for i in 0..n {
304 s.push_str(&format!("record r{}:\n owner as address.private;\n", i));
305 for j in 0..10 {
306 s.push_str(&format!(" {}{} as u128.private;\n", var_name, j));
307 }
308 }
309 s
310 };
311
312 let gen_mapping_string = |n: usize| -> String {
314 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
315 for i in 0..n {
316 s.push_str(&format!(
317 "mapping {}{}:\n key as field.public;\n value as field.public;\n",
318 var_name, i
319 ));
320 }
321 s
322 };
323
324 let gen_closure_string = |n: usize| -> String {
326 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
327 for i in 0..n {
328 s.push_str(&format!("closure c{}:\n input r0 as u128;\n", i));
329 for j in 0..10 {
330 s.push_str(&format!(" add r0 r0 into r{};\n", j));
331 }
332 s.push_str(&format!(" output r{} as u128;\n", 4000));
333 }
334 s
335 };
336
337 let gen_function_string = |n: usize| -> String {
339 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
340 for i in 0..n {
341 s.push_str(&format!("function f{}:\n add 1u128 1u128 into r0;\n", i));
342 for j in 0..10 {
343 s.push_str(&format!(" add r0 r0 into r{j};\n"));
344 }
345 }
346 s
347 };
348
349 let test_parse = |imports: &str, body: &str, should_succeed: bool| {
351 let program = format!("{imports}\nprogram to_parse.aleo;\n\n{body}");
352 let result = Program::<CurrentNetwork>::from_str(&program);
353 if result.is_ok() != should_succeed {
354 println!("Program failed to parse: {program}");
355 }
356 assert_eq!(result.is_ok(), should_succeed);
357 };
358
359 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS), &gen_struct_string(1), true);
361 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS + 1), &gen_struct_string(1), false);
363 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS), true);
365 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS + 1), false);
367 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS), true);
369 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS + 1), false);
371 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS), true);
373 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS + 1), false);
375 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES), true);
377 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES + 1), false);
379 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS), true);
381 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS + 1), false);
383
384 let program_too_big = format!(
386 "{} {} {} {} {}",
387 gen_struct_string(CurrentNetwork::MAX_STRUCTS),
388 gen_record_string(CurrentNetwork::MAX_RECORDS),
389 gen_mapping_string(CurrentNetwork::MAX_MAPPINGS),
390 gen_closure_string(CurrentNetwork::MAX_CLOSURES),
391 gen_function_string(CurrentNetwork::MAX_FUNCTIONS)
392 );
393 test_parse("", &program_too_big, false);
395 }
396}