snarkvm_synthesizer_program/logic/instruction/
parse.rs

1// Copyright 2024-2025 Aleo Network Foundation
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 Instruction<N> {
19    /// Parses a string into an instruction.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        /// Create an alt parser that matches the instruction.
23        ///
24        /// `nom` documentation notes that alt supports a maximum of 21 parsers.
25        /// The documentation suggests to nest alt to support more parsers, as we do here.
26        /// Note that order of the individual parsers matters.
27        macro_rules! alt_parser {
28            ($v0:expr) => {{ alt(($v0,)) }};
29            ($v0:expr, $v1:expr) => {{ alt(($v0, $v1,)) }};
30            ($v0:expr, $v1:expr, $v2:expr) => {{ alt(($v0, $v1, $v2,)) }};
31            ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{ alt(($v0, $v1, $v2, $v3,)) }};
32            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4,)) }};
33            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4, $v5,)) }};
34            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4, $v5, $v6,)) }};
35            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr, $v7:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7,)) }};
36            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr, $v7:expr, $v8:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7, $v8,)) }};
37            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr, $v7:expr, $v8:expr, $v9:expr) => {{ alt(($v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7, $v8, $v9,)) }};
38            ($v0:expr, $v1:expr, $v2:expr, $v3:expr, $v4:expr, $v5:expr, $v6:expr, $v7:expr, $v8:expr, $v9:expr, $( $variants:expr ),*) => {{ alt((
39                alt_parser!($( $variants ),*), $v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7, $v8, $v9,
40            )) }};
41        }
42
43        /// Creates a parser for the given instructions.
44        ///
45        /// ## Example
46        /// ```ignore
47        /// instruction_parsers!(self, |_instruction| {}, { Add, Sub, Mul, Div })
48        /// ```
49        macro_rules! instruction_parsers {
50            ($object:expr, |_instruction| $_operation:block, { $( $variant:ident, )+ }) => {{
51                alt_parser!( $( map($variant::parse, Into::into) ),+ )
52            }};
53        }
54
55        // Parse the whitespace and comments from the string.
56        let (string, _) = Sanitizer::parse(string)?;
57        // Parse the instruction from the string.
58        let (string, instruction) = crate::instruction!(instruction_parsers!(self, _instruction))(string)?;
59        // Parse the whitespace from the string.
60        let (string, _) = Sanitizer::parse_whitespaces(string)?;
61        // Parse the semicolon from the string.
62        let (string, _) = tag(";")(string)?;
63
64        Ok((string, instruction))
65    }
66}
67
68impl<N: Network> FromStr for Instruction<N> {
69    type Err = Error;
70
71    /// Parses a string into an instruction.
72    #[inline]
73    fn from_str(string: &str) -> Result<Self> {
74        match Self::parse(string) {
75            Ok((remainder, object)) => {
76                // Ensure the remainder is empty.
77                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
78                // Return the object.
79                Ok(object)
80            }
81            Err(error) => bail!("Failed to parse string. {error}"),
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use console::network::MainnetV0;
90
91    type CurrentNetwork = MainnetV0;
92
93    #[test]
94    fn test_parse() -> Result<()> {
95        let instruction = "add r0 r1 into r2;";
96        let (remainder, candidate) = Instruction::<CurrentNetwork>::parse(instruction)?;
97        assert_eq!("", remainder);
98        assert_eq!(instruction, candidate.to_string());
99        Ok(())
100    }
101}