snarkvm_synthesizer_program/function/
parse.rs1use super::*;
17
18impl<N: Network> Parser for FunctionCore<N> {
19 #[inline]
21 fn parse(string: &str) -> ParserResult<Self> {
22 let (string, _) = Sanitizer::parse(string)?;
24 let (string, _) = tag(Self::type_name())(string)?;
26 let (string, _) = Sanitizer::parse_whitespaces(string)?;
28 let (string, name) = Identifier::<N>::parse(string)?;
30 let (string, _) = Sanitizer::parse_whitespaces(string)?;
32 let (string, _) = tag(":")(string)?;
34
35 let (string, inputs) = many0(Input::parse)(string)?;
37 let (string, instructions) = many0(Instruction::parse)(string)?;
39 let (string, outputs) = many0(Output::parse)(string)?;
41
42 let (string, finalize) = opt(FinalizeCore::parse)(string)?;
44
45 map_res(take(0usize), move |_| {
46 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
73impl<N: Network> FromStr for FunctionCore<N> {
74 type Err = Error;
75
76 fn from_str(string: &str) -> Result<Self> {
78 match Self::parse(string) {
79 Ok((remainder, object)) => {
80 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
82 Ok(object)
84 }
85 Err(error) => bail!("Failed to parse string. {error}"),
86 }
87 }
88}
89
90impl<N: Network> Debug for FunctionCore<N> {
91 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
93 Display::fmt(self, f)
94 }
95}
96
97impl<N: Network> Display for FunctionCore<N> {
98 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
100 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 let Some(finalize) = &self.finalize_logic {
108 write!(f, "\n\n")?;
109 write!(f, "{finalize}")?;
110 }
111 Ok(())
112 }
113}
114
115#[cfg(test)]
116mod 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"
127function 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 let function = Function::<CurrentNetwork>::parse(
142 r"
143function 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"
159function 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"
176function 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"
191function 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.aleo/mint_public.future;
200
201// The finalize scope of `mint_public` increments the
202// `account` of the token receiver by the specified amount.
203finalize 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"
228function 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.aleo/foo.future;
233
234finalize 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"
250function 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.aleo/compute.future;
257
258finalize 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}