snarkvm_synthesizer_program/closure/
parse.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM 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
16use super::*;
17
18impl<N: Network> Parser for ClosureCore<N> {
19    /// Parses a string into a closure.
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 'closure' 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 closure 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) = many1(Input::parse)(string)?;
37        // Parse the instructions from the string.
38        let (string, instructions) = many1(Instruction::parse)(string)?;
39        // Parse the outputs from the string.
40        let (string, outputs) = many0(Output::parse)(string)?;
41
42        map_res(take(0usize), move |_| {
43            // Initialize a new closure.
44            let mut closure = Self::new(name);
45            inputs.iter().cloned().try_for_each(|input| closure.add_input(input))?;
46            instructions.iter().cloned().try_for_each(|instruction| closure.add_instruction(instruction))?;
47            outputs.iter().cloned().try_for_each(|output| closure.add_output(output))?;
48            Ok::<_, Error>(closure)
49        })(string)
50    }
51}
52
53impl<N: Network> FromStr for ClosureCore<N> {
54    type Err = Error;
55
56    /// Returns a closure from a string literal.
57    fn from_str(string: &str) -> Result<Self> {
58        match Self::parse(string) {
59            Ok((remainder, object)) => {
60                // Ensure the remainder is empty.
61                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
62                // Return the object.
63                Ok(object)
64            }
65            Err(error) => bail!("Failed to parse string. {error}"),
66        }
67    }
68}
69
70impl<N: Network> Debug for ClosureCore<N> {
71    /// Prints the closure as a string.
72    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
73        Display::fmt(self, f)
74    }
75}
76
77impl<N: Network> Display for ClosureCore<N> {
78    /// Prints the closure as a string.
79    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
80        // Write the closure to a string.
81        write!(f, "{} {}:", Self::type_name(), self.name)?;
82        self.inputs.iter().try_for_each(|input| write!(f, "\n    {input}"))?;
83        self.instructions.iter().try_for_each(|instruction| write!(f, "\n    {instruction}"))?;
84        self.outputs.iter().try_for_each(|output| write!(f, "\n    {output}"))
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::Closure;
92    use console::network::MainnetV0;
93
94    type CurrentNetwork = MainnetV0;
95
96    #[test]
97    fn test_closure_parse() {
98        let closure = Closure::<CurrentNetwork>::parse(
99            r"
100closure foo:
101    input r0 as field;
102    input r1 as field;
103    add r0 r1 into r2;
104    output r2 as field;",
105        )
106        .unwrap()
107        .1;
108        assert_eq!("foo", closure.name().to_string());
109        assert_eq!(2, closure.inputs().len());
110        assert_eq!(1, closure.instructions().len());
111        assert_eq!(1, closure.outputs().len());
112    }
113
114    #[test]
115    fn test_closure_parse_cast() {
116        let closure = Closure::<CurrentNetwork>::parse(
117            r"
118closure foo:
119    input r0 as token.record;
120    cast r0.owner r0.token_amount into r1 as data;
121    output r1 as data;",
122        )
123        .unwrap()
124        .1;
125        assert_eq!("foo", closure.name().to_string());
126        assert_eq!(1, closure.inputs().len());
127        assert_eq!(1, closure.instructions().len());
128        assert_eq!(1, closure.outputs().len());
129    }
130
131    #[test]
132    fn test_closure_display() {
133        let expected = r"closure foo:
134    input r0 as field;
135    input r1 as field;
136    add r0 r1 into r2;
137    output r2 as field;";
138        let closure = Closure::<CurrentNetwork>::parse(expected).unwrap().1;
139        assert_eq!(expected, format!("{closure}"),);
140    }
141
142    #[test]
143    fn test_closure_parse_output_function() {
144        let result = Closure::<CurrentNetwork>::parse(
145            r"
146closure foo:
147    input r0 as token.record;
148    cast r0.owner r0.token_amount into r1 as token.record;
149    output r1 as token.record;",
150        );
151
152        assert!(result.is_err());
153    }
154}