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