snarkvm_synthesizer_program/
parse.rs1use super::*;
17
18impl<N: Network> Parser for ProgramCore<N> {
19 #[inline]
21 fn parse(string: &str) -> ParserResult<Self> {
22 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 let (string, imports) = many0(Import::parse)(string)?;
34 let (string, _) = Sanitizer::parse(string)?;
36 let (string, _) = tag(Self::type_name())(string)?;
38 let (string, _) = Sanitizer::parse_whitespaces(string)?;
40 let (string, id) = ProgramID::parse(string)?;
42 let (string, _) = Sanitizer::parse_whitespaces(string)?;
44 let (string, _) = tag(";")(string)?;
46
47 fn intermediate<N: Network>(string: &str) -> ParserResult<P<N>> {
48 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 let (string, components) = many1(intermediate)(string)?;
70 let (string, _) = Sanitizer::parse(string)?;
72
73 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
82 for import in imports {
84 match program.add_import(import) {
85 Ok(_) => (),
86 Err(error) => {
87 eprintln!("{error}");
88 return map_res(take(0usize), Err)(string);
89 }
90 }
91 }
92
93 for component in components {
95 let result = match component {
96 P::Constructor(constructor) => program.add_constructor(constructor),
97 P::M(mapping) => program.add_mapping(mapping),
98 P::S(struct_) => program.add_struct(struct_),
99 P::R(record) => program.add_record(record),
100 P::C(closure) => program.add_closure(closure),
101 P::F(function) => program.add_function(function),
102 };
103
104 match result {
105 Ok(_) => (),
106 Err(error) => {
107 eprintln!("{error}");
108 return map_res(take(0usize), Err)(string);
109 }
110 }
111 }
112
113 Ok((string, program))
114 }
115}
116
117impl<N: Network> FromStr for ProgramCore<N> {
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> Debug for ProgramCore<N> {
138 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
140 Display::fmt(self, f)
141 }
142}
143
144impl<N: Network> Display for ProgramCore<N> {
145 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
147 if !self.imports.is_empty() {
148 for import in self.imports.values() {
150 writeln!(f, "{import}")?;
151 }
152
153 writeln!(f)?;
155 }
156
157 write!(f, "{} {};\n\n", Self::type_name(), self.id)?;
159
160 let mut components_iter = self.components.iter().peekable();
162 while let Some((label, definition)) = components_iter.next() {
163 match label {
164 ProgramLabel::Constructor => {
165 if let Some(constructor) = &self.constructor {
167 writeln!(f, "{constructor}")?;
168 }
169 }
170 ProgramLabel::Identifier(identifier) => match definition {
171 ProgramDefinition::Constructor => return Err(fmt::Error),
172 ProgramDefinition::Mapping => match self.mappings.get(identifier) {
173 Some(mapping) => writeln!(f, "{mapping}")?,
174 None => return Err(fmt::Error),
175 },
176 ProgramDefinition::Struct => match self.structs.get(identifier) {
177 Some(struct_) => writeln!(f, "{struct_}")?,
178 None => return Err(fmt::Error),
179 },
180 ProgramDefinition::Record => match self.records.get(identifier) {
181 Some(record) => writeln!(f, "{record}")?,
182 None => return Err(fmt::Error),
183 },
184 ProgramDefinition::Closure => match self.closures.get(identifier) {
185 Some(closure) => writeln!(f, "{closure}")?,
186 None => return Err(fmt::Error),
187 },
188 ProgramDefinition::Function => match self.functions.get(identifier) {
189 Some(function) => writeln!(f, "{function}")?,
190 None => return Err(fmt::Error),
191 },
192 },
193 }
194
195 if components_iter.peek().is_some() {
197 writeln!(f)?;
198 }
199 }
200
201 Ok(())
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use crate::Program;
209 use console::network::MainnetV0;
210
211 type CurrentNetwork = MainnetV0;
212
213 #[test]
214 fn test_program_parse() -> Result<()> {
215 let (string, program) = Program::<CurrentNetwork>::parse(
217 r"
218program to_parse.aleo;
219
220struct message:
221 first as field;
222 second as field;
223
224function compute:
225 input r0 as message.private;
226 add r0.first r0.second into r1;
227 output r1 as field.private;",
228 )
229 .unwrap();
230 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
231
232 assert!(program.contains_struct(&Identifier::from_str("message")?));
234 assert!(program.contains_function(&Identifier::from_str("compute")?));
236
237 Ok(())
238 }
239
240 #[test]
241 fn test_program_parse_function_zero_inputs() -> Result<()> {
242 let (string, program) = Program::<CurrentNetwork>::parse(
244 r"
245program to_parse.aleo;
246
247function compute:
248 add 1u32 2u32 into r0;
249 output r0 as u32.private;",
250 )
251 .unwrap();
252 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
253
254 assert!(program.contains_function(&Identifier::from_str("compute")?));
256
257 Ok(())
258 }
259
260 #[test]
261 fn test_program_display() -> Result<()> {
262 let expected = r"program to_parse.aleo;
263
264struct message:
265 first as field;
266 second as field;
267
268function compute:
269 input r0 as message.private;
270 add r0.first r0.second into r1;
271 output r1 as field.private;
272";
273 let program = Program::<CurrentNetwork>::from_str(expected)?;
275 assert_eq!(expected, format!("{program}"));
277
278 Ok(())
279 }
280
281 #[test]
282 fn test_program_size() {
283 let var_name = "a";
285
286 let gen_import_string = |n: usize| -> String {
288 let mut s = String::new();
289 for i in 0..n {
290 s.push_str(&format!("import foo{i}.aleo;\n"));
291 }
292 s
293 };
294
295 let gen_struct_string = |n: usize| -> String {
297 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
298 for i in 0..n {
299 s.push_str(&format!("struct m{i}:\n"));
300 for j in 0..10 {
301 s.push_str(&format!(" {var_name}{j} as u128;\n"));
302 }
303 }
304 s
305 };
306
307 let gen_record_string = |n: usize| -> String {
309 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
310 for i in 0..n {
311 s.push_str(&format!("record r{i}:\n owner as address.private;\n"));
312 for j in 0..10 {
313 s.push_str(&format!(" {var_name}{j} as u128.private;\n"));
314 }
315 }
316 s
317 };
318
319 let gen_mapping_string = |n: usize| -> String {
321 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
322 for i in 0..n {
323 s.push_str(&format!("mapping {var_name}{i}:\n key as field.public;\n value as field.public;\n"));
324 }
325 s
326 };
327
328 let gen_closure_string = |n: usize| -> String {
330 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
331 for i in 0..n {
332 s.push_str(&format!("closure c{i}:\n input r0 as u128;\n"));
333 for j in 0..10 {
334 s.push_str(&format!(" add r0 r0 into r{j};\n"));
335 }
336 s.push_str(&format!(" output r{} as u128;\n", 4000));
337 }
338 s
339 };
340
341 let gen_function_string = |n: usize| -> String {
343 let mut s = String::with_capacity(CurrentNetwork::MAX_PROGRAM_SIZE);
344 for i in 0..n {
345 s.push_str(&format!("function f{i}:\n add 1u128 1u128 into r0;\n"));
346 for j in 0..10 {
347 s.push_str(&format!(" add r0 r0 into r{j};\n"));
348 }
349 }
350 s
351 };
352
353 let test_parse = |imports: &str, body: &str, should_succeed: bool| {
355 let program = format!("{imports}\nprogram to_parse.aleo;\n\n{body}");
356 let result = Program::<CurrentNetwork>::from_str(&program);
357 if result.is_ok() != should_succeed {
358 println!("Program failed to parse: {program}");
359 }
360 assert_eq!(result.is_ok(), should_succeed);
361 };
362
363 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS), &gen_struct_string(1), true);
365 test_parse(&gen_import_string(CurrentNetwork::MAX_IMPORTS + 1), &gen_struct_string(1), false);
367 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS), true);
369 test_parse("", &gen_struct_string(CurrentNetwork::MAX_STRUCTS + 1), false);
371 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS), true);
373 test_parse("", &gen_record_string(CurrentNetwork::MAX_RECORDS + 1), false);
375 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS), true);
377 test_parse("", &gen_mapping_string(CurrentNetwork::MAX_MAPPINGS + 1), false);
379 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES), true);
381 test_parse("", &gen_closure_string(CurrentNetwork::MAX_CLOSURES + 1), false);
383 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS), true);
385 test_parse("", &gen_function_string(CurrentNetwork::MAX_FUNCTIONS + 1), false);
387
388 let program_too_big = format!(
390 "{} {} {} {} {}",
391 gen_struct_string(CurrentNetwork::MAX_STRUCTS),
392 gen_record_string(CurrentNetwork::MAX_RECORDS),
393 gen_mapping_string(CurrentNetwork::MAX_MAPPINGS),
394 gen_closure_string(CurrentNetwork::MAX_CLOSURES),
395 gen_function_string(CurrentNetwork::MAX_FUNCTIONS)
396 );
397 test_parse("", &program_too_big, false);
399 }
400}