/ synthesizer / program / src / function / parse.rs
parse.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaVM 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 FunctionCore<N> {
 19      /// Parses a string into a function.
 20      #[inline]
 21      fn parse(string: &str) -> ParserResult<Self> {
 22          // Parse the whitespace and comments from the string.
 23          let (string, _) = Sanitizer::parse(string)?;
 24          // Parse the 'function' keyword from the string.
 25          let (string, _) = tag(Self::type_name())(string)?;
 26          // Parse the whitespace from the string.
 27          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 28          // Parse the function name from the string.
 29          let (string, name) = Identifier::<N>::parse(string)?;
 30          // Parse the whitespace from the string.
 31          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 32          // Parse the colon ':' keyword from the string.
 33          let (string, _) = tag(":")(string)?;
 34  
 35          // Parse the inputs from the string.
 36          let (string, inputs) = many0(Input::parse)(string)?;
 37          // Parse the instructions from the string.
 38          let (string, instructions) = many0(Instruction::parse)(string)?;
 39          // Parse the outputs from the string.
 40          let (string, outputs) = many0(Output::parse)(string)?;
 41  
 42          // Parse an optional finalize command from the string.
 43          let (string, finalize) = opt(FinalizeCore::parse)(string)?;
 44  
 45          map_res(take(0usize), move |_| {
 46              // Initialize a new function.
 47              let mut function = Self::new(name);
 48              if let Err(error) = inputs.iter().cloned().try_for_each(|input| function.add_input(input)) {
 49                  eprintln!("{error}");
 50                  return Err(error);
 51              }
 52              if let Err(error) =
 53                  instructions.iter().cloned().try_for_each(|instruction| function.add_instruction(instruction))
 54              {
 55                  eprintln!("{error}");
 56                  return Err(error);
 57              }
 58              if let Err(error) = outputs.iter().cloned().try_for_each(|output| function.add_output(output)) {
 59                  eprintln!("{error}");
 60                  return Err(error);
 61              }
 62              if let Some(finalize) = &finalize {
 63                  if let Err(error) = function.add_finalize(finalize.clone()) {
 64                      eprintln!("{error}");
 65                      return Err(error);
 66                  }
 67              }
 68              Ok::<_, Error>(function)
 69          })(string)
 70      }
 71  }
 72  
 73  impl<N: Network> FromStr for FunctionCore<N> {
 74      type Err = Error;
 75  
 76      /// Returns a function from a string literal.
 77      fn from_str(string: &str) -> Result<Self> {
 78          match Self::parse(string) {
 79              Ok((remainder, object)) => {
 80                  // Ensure the remainder is empty.
 81                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
 82                  // Return the object.
 83                  Ok(object)
 84              }
 85              Err(error) => bail!("Failed to parse string. {error}"),
 86          }
 87      }
 88  }
 89  
 90  impl<N: Network> Debug for FunctionCore<N> {
 91      /// Prints the function as a string.
 92      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
 93          Display::fmt(self, f)
 94      }
 95  }
 96  
 97  impl<N: Network> Display for FunctionCore<N> {
 98      /// Prints the function as a string.
 99      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100          // Write the function to a string.
101          write!(f, "{} {}:", Self::type_name(), self.name)?;
102          self.inputs.iter().try_for_each(|input| write!(f, "\n    {input}"))?;
103          self.instructions.iter().try_for_each(|instruction| write!(f, "\n    {instruction}"))?;
104          self.outputs.iter().try_for_each(|output| write!(f, "\n    {output}"))?;
105  
106          // If finalize exists, write it out.
107          if let Some(finalize) = &self.finalize_logic {
108              write!(f, "\n\n")?;
109              write!(f, "{finalize}")?;
110          }
111          Ok(())
112      }
113  }
114  
115  #[cfg(test)]
116  mod tests {
117      use super::*;
118      use crate::Function;
119      use console::network::MainnetV0;
120  
121      type CurrentNetwork = MainnetV0;
122  
123      #[test]
124      fn test_function_parse() {
125          let function = Function::<CurrentNetwork>::parse(
126              r"
127  function foo:
128      input r0 as field.public;
129      input r1 as field.private;
130      add r0 r1 into r2;
131      output r2 as field.private;",
132          )
133          .unwrap()
134          .1;
135          assert_eq!("foo", function.name().to_string());
136          assert_eq!(2, function.inputs().len());
137          assert_eq!(1, function.instructions().len());
138          assert_eq!(1, function.outputs().len());
139  
140          // Function with 0 inputs.
141          let function = Function::<CurrentNetwork>::parse(
142              r"
143  function foo:
144      add 1u32 2u32 into r0;
145      output r0 as u32.private;",
146          )
147          .unwrap()
148          .1;
149          assert_eq!("foo", function.name().to_string());
150          assert_eq!(0, function.inputs().len());
151          assert_eq!(1, function.instructions().len());
152          assert_eq!(1, function.outputs().len());
153      }
154  
155      #[test]
156      fn test_function_parse_cast() {
157          let function = Function::<CurrentNetwork>::parse(
158              r"
159  function foo:
160      input r0 as token.record;
161      cast r0.owner r0.token_amount into r1 as token.record;
162      output r1 as token.record;",
163          )
164          .unwrap()
165          .1;
166          assert_eq!("foo", function.name().to_string());
167          assert_eq!(1, function.inputs().len());
168          assert_eq!(1, function.instructions().len());
169          assert_eq!(1, function.outputs().len());
170      }
171  
172      #[test]
173      fn test_function_parse_no_instruction_or_output() {
174          let function = Function::<CurrentNetwork>::parse(
175              r"
176  function foo:
177      input r0 as token.record;",
178          )
179          .unwrap()
180          .1;
181          assert_eq!("foo", function.name().to_string());
182          assert_eq!(1, function.inputs().len());
183          assert_eq!(0, function.instructions().len());
184          assert_eq!(0, function.outputs().len());
185      }
186  
187      #[test]
188      fn test_function_parse_finalize() {
189          let function = Function::<CurrentNetwork>::parse(
190              r"
191  function mint_public:
192      // Input the token receiver.
193      input r0 as address.public;
194      // Input the token amount.
195      input r1 as u64.public;
196      // Mint the tokens via an asynchronous call.
197      async mint_public r0 r1 into r2;
198      // Output the future.
199      output r2 as foo.alpha/mint_public.future;
200  
201  // The finalize scope of `mint_public` increments the
202  // `account` of the token receiver by the specified amount.
203  finalize mint_public:
204      // Input the token receiver.
205      input r0 as address.public;
206      // Input the token amount.
207      input r1 as u64.public;
208  
209      // Get `account[r0]` into `r2`, defaulting to 0u64 if the entry does not exist.
210      get.or_use account[r0] 0u64 into r2;
211      // Add `r1` to `r2`. If the operation overflows, `mint_public` is reverted.
212      add r2 r1 into r3;
213      // Set `r3` into `account[r0]`.
214      set r3 into account[r0];
215  ",
216          )
217          .unwrap()
218          .1;
219          assert_eq!("mint_public", function.name().to_string());
220          assert_eq!(2, function.inputs().len());
221          assert_eq!(1, function.instructions().len());
222          assert_eq!(1, function.outputs().len());
223          assert_eq!(2, function.finalize_logic().as_ref().unwrap().inputs().len());
224          assert_eq!(3, function.finalize_logic().as_ref().unwrap().commands().len());
225  
226          let function = Function::<CurrentNetwork>::parse(
227              r"
228  function foo:
229      input r0 as token.record;
230      cast r0.owner r0.token_amount into r1 as token.record;
231      async foo r1.token_amount into r2;
232      output r2 as foo.alpha/foo.future;
233  
234  finalize foo:
235      input r0 as u64.public;
236      add r0 r0 into r1;
237  ",
238          )
239          .unwrap()
240          .1;
241          assert_eq!("foo", function.name().to_string());
242          assert_eq!(1, function.inputs().len());
243          assert_eq!(2, function.instructions().len());
244          assert_eq!(1, function.outputs().len());
245          assert_eq!(1, function.finalize_logic().as_ref().unwrap().inputs().len());
246          assert_eq!(1, function.finalize_logic().as_ref().unwrap().commands().len());
247  
248          let function = Function::<CurrentNetwork>::parse(
249              r"
250  function compute:
251      input r0 as address.public;
252      input r1 as u64.public;
253      input r2 as u64.public;
254      add r1 r2 into r3;
255      async compute r0 r3 into r4;
256      output r4 as foo.alpha/compute.future;
257  
258  finalize compute:
259      input r0 as address.public;
260      input r1 as u64.public;
261      get.or_use account[r0] 0u64 into r2;
262      add r2 r1 into r3;
263      set r3 into account[r0];
264      ",
265          )
266          .unwrap()
267          .1;
268          assert_eq!(3, function.inputs().len());
269          assert_eq!(2, function.instructions().len());
270          assert_eq!(1, function.outputs().len());
271          assert_eq!(2, function.finalize_logic().as_ref().unwrap().inputs().len());
272          assert_eq!(3, function.finalize_logic().as_ref().unwrap().commands().len());
273      }
274  
275      #[test]
276      fn test_function_display() {
277          let expected = r"function foo:
278      input r0 as field.public;
279      input r1 as field.private;
280      add r0 r1 into r2;
281      output r2 as field.private;";
282          let function = Function::<CurrentNetwork>::parse(expected).unwrap().1;
283          assert_eq!(expected, format!("{function}"),);
284      }
285  }