/ synthesizer / program / src / parse.rs
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  }