diff --git a/compiler/parser/src/parser/expression.rs b/compiler/parser/src/parser/expression.rs index 627777cfb3..147d0f433c 100644 --- a/compiler/parser/src/parser/expression.rs +++ b/compiler/parser/src/parser/expression.rs @@ -498,7 +498,7 @@ impl ParserContext<'_> { /// - self /// /// Returns an expression error if the token cannot be matched. - fn parse_primary_expression(&mut self) -> Result { + pub fn parse_primary_expression(&mut self) -> Result { if let Token::LeftParen = self.token.token { return self.parse_tuple_expression(); } diff --git a/compiler/parser/src/parser/instruction.rs b/compiler/parser/src/parser/instruction.rs index 8d040abb5a..77cb21d264 100644 --- a/compiler/parser/src/parser/instruction.rs +++ b/compiler/parser/src/parser/instruction.rs @@ -14,8 +14,9 @@ // You should have received a copy of the GNU General Public License // along with the Leo library. If not, see . -// TODO: Consider designing a separate Parser for instructions. This would improve modularity at the -// cost of building/managing the new parser. +// TODO: We currently rely on the Leo tokenizer and parser to parse the assembly block. +// Consider designing a separate Parser for instructions. +// This would improve modularity, reduce the likelihood of errors, improve error handling, at the cost of building, linking, and managing the new parser. // TODO: If snarkVM instructions are used directly, then we should directly use the associated instruction parsers. use crate::{ParserContext, Token}; @@ -25,31 +26,40 @@ use leo_ast::{ LessThanOrEqual, Mul, Node, Nop, Not, Operand, Or, Sub, Ternary, }; use leo_errors::{ParserError, Result}; -use leo_span::Span; +use leo_span::{sym, Span}; -// TODO: Note that this design is a prototype. impl ParserContext<'_> { pub fn parse_instruction(&mut self) -> Result { // Parse the opcode. Since we are using the Leo tokenizer, the opcode will be tokenized as an identifier. let identifier = self.expect_ident()?; - match &*identifier.name.as_str() { - "add" => self.parse_add_instruction(identifier.span), - "and" => self.parse_and_instruction(identifier.span), - "div" => self.parse_div_instruction(identifier.span), - "gt" => self.parse_greater_than_instruction(identifier.span), - "gte" => self.parse_greater_than_or_equal_instruction(identifier.span), - "eq" => self.parse_equal_instruction(identifier.span), - "neq" => self.parse_not_equal_instruction(identifier.span), - "lt" => self.parse_less_than_instruction(identifier.span), - "lte" => self.parse_less_than_or_equal_instruction(identifier.span), - "mul" => self.parse_mul_instruction(identifier.span), - "not" => self.parse_not_instruction(identifier.span), - "or" => self.parse_or_instruction(identifier.span), - "sub" => self.parse_sub_instruction(identifier.span), - "ter" => self.parse_ternary_instruction(identifier.span), + match identifier.name { + sym::add => self.parse_add_instruction(identifier.span), + sym::and => self.parse_and_instruction(identifier.span), + sym::div => self.parse_div_instruction(identifier.span), + sym::gt => self.parse_greater_than_instruction(identifier.span), + sym::gte => self.parse_greater_than_or_equal_instruction(identifier.span), + sym::eq => self.parse_equal_instruction(identifier.span), + sym::neq => self.parse_not_equal_instruction(identifier.span), + sym::lt => self.parse_less_than_instruction(identifier.span), + sym::lte => self.parse_less_than_or_equal_instruction(identifier.span), + sym::mul => self.parse_mul_instruction(identifier.span), + sym::not => self.parse_not_instruction(identifier.span), + sym::or => self.parse_or_instruction(identifier.span), + sym::sub => self.parse_sub_instruction(identifier.span), + sym::ter => self.parse_ternary_instruction(identifier.span), _ => { self.emit_err(ParserError::invalid_opcode_in_assembly_instruction(identifier.span)); - // TODO: Do we need to eat tokens here? + // Attempt to recover the parser by eating tokens until we find a semi-colon or closing bracket. + while !(self.check(&Token::Eof) || self.check(&Token::RightCurly) || self.check(&Token::Semicolon)) { + self.bump(); + } + if let Ok(span) = self.expect(&Token::Eof) { + self.emit_err(ParserError::unexpected_eof(span)); + } else if let Ok(span) = self.expect(&Token::RightCurly) { + self.emit_err(ParserError::unexpected_end_of_assembly_block(span)); + } else { + self.expect(&Token::Semicolon)?; + } Ok(Instruction::Nop(Nop { span: identifier.span })) } } @@ -253,18 +263,16 @@ impl ParserContext<'_> { })) } - // TODO: Better error handling. - // Separate tokens and symbols for assembly block. pub fn expect_into(&mut self) -> Result<()> { let identifier = self.expect_ident()?; - match &*identifier.name.as_str() { - "into" => Ok(()), - string => Err(ParserError::unexpected(string, "into", identifier.span).into()), + match identifier.name { + sym::into => Ok(()), + symbol => Err(ParserError::unexpected(symbol.as_str(), "`into`", identifier.span).into()), } } pub fn parse_operand(&mut self) -> Result { - let expression = self.parse_expression()?; + let expression = self.parse_primary_expression()?; match expression { Expression::Identifier(identifier) => Ok(Operand::Identifier(identifier)), Expression::Literal(literal) => Ok(Operand::Literal(literal)), diff --git a/leo/errors/src/errors/parser/parser_errors.rs b/leo/errors/src/errors/parser/parser_errors.rs index 398c926b9a..c8d72dbebf 100644 --- a/leo/errors/src/errors/parser/parser_errors.rs +++ b/leo/errors/src/errors/parser/parser_errors.rs @@ -426,4 +426,12 @@ create_messages!( msg: format!("Invalid operand in assembly block. Operands must be `Identifier`s or `LiteralExpressions`."), help: None, } + + /// For when the parser encountered an unexpected end of assembly block. + @formatted + unexpected_end_of_assembly_block { + args: (), + msg: "Unexpected end of assembly block.", + help: None, + } ); diff --git a/leo/span/src/symbol.rs b/leo/span/src/symbol.rs index c8373237dd..b0e0e3bc5f 100644 --- a/leo/span/src/symbol.rs +++ b/leo/span/src/symbol.rs @@ -137,6 +137,9 @@ symbols! { sub_wrapped, xor, + // ternary operators + ter, + // core circuits BHP256, BHP512, @@ -221,6 +224,9 @@ symbols! { state_leaf, public, private, + + // Assembly specific symbols + into, } /// An interned string. diff --git a/tests/expectations/parser/parser/statement/assembly_block.out b/tests/expectations/parser/parser/statement/assembly_block.out index b5c08bc7e3..bb89ad9403 100644 --- a/tests/expectations/parser/parser/statement/assembly_block.out +++ b/tests/expectations/parser/parser/statement/assembly_block.out @@ -14,81 +14,67 @@ outputs: hi: 11 - AssemblyBlock: instructions: - - opcode: Add - operands: - - Identifier: "{\"name\":\"r1\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":17}\"}" - - Identifier: "{\"name\":\"r2\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":20}\"}" - destinations: - - "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":26,\\\"hi\\\":28}\"}" - span: - lo: 11 - hi: 29 + - Add: + first: + Identifier: "{\"name\":\"r1\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":17}\"}" + second: + Identifier: "{\"name\":\"r2\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":20}\"}" + destination: "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":26,\\\"hi\\\":28}\"}" + span: + lo: 11 + hi: 28 span: lo: 0 hi: 31 - AssemblyBlock: instructions: - - opcode: Add - operands: - - Identifier: "{\"name\":\"r1\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":17}\"}" - - Identifier: "{\"name\":\"r2\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":20}\"}" - - Identifier: "{\"name\":\"thisistreatedasanidentifier\",\"span\":\"{\\\"lo\\\":21,\\\"hi\\\":48}\"}" - - Identifier: "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":49,\\\"hi\\\":51}\"}" - destinations: [] - span: - lo: 11 - hi: 52 - span: - lo: 0 - hi: 54 - - AssemblyBlock: - instructions: - - opcode: Add - operands: - - Identifier: "{\"name\":\"r1\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":17}\"}" - - Identifier: "{\"name\":\"r2\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":20}\"}" - destinations: - - "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":26,\\\"hi\\\":28}\"}" - span: - lo: 11 - hi: 29 - - opcode: IsEqual - operands: - - Identifier: "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":33,\\\"hi\\\":35}\"}" - - Literal: + - Add: + first: + Identifier: "{\"name\":\"r1\",\"span\":\"{\\\"lo\\\":15,\\\"hi\\\":17}\"}" + second: + Identifier: "{\"name\":\"r2\",\"span\":\"{\\\"lo\\\":18,\\\"hi\\\":20}\"}" + destination: "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":26,\\\"hi\\\":28}\"}" + span: + lo: 11 + hi: 28 + - IsEqual: + first: + Identifier: "{\"name\":\"r3\",\"span\":\"{\\\"lo\\\":33,\\\"hi\\\":35}\"}" + second: + Literal: Integer: - U8 - "0" - span: lo: 36 hi: 39 - destinations: - - "{\"name\":\"r4\",\"span\":\"{\\\"lo\\\":45,\\\"hi\\\":47}\"}" - span: - lo: 30 - hi: 48 - - opcode: Ternary - operands: - - Identifier: "{\"name\":\"r4\",\"span\":\"{\\\"lo\\\":53,\\\"hi\\\":55}\"}" - - Literal: + destination: "{\"name\":\"r4\",\"span\":\"{\\\"lo\\\":45,\\\"hi\\\":47}\"}" + span: + lo: 30 + hi: 47 + - Ternary: + first: + Identifier: "{\"name\":\"r4\",\"span\":\"{\\\"lo\\\":53,\\\"hi\\\":55}\"}" + second: + Literal: Integer: - U8 - "1" - span: lo: 56 hi: 59 - - Literal: + third: + Literal: Integer: - U8 - "2" - span: lo: 60 hi: 63 - destinations: - - "{\"name\":\"r5\",\"span\":\"{\\\"lo\\\":69,\\\"hi\\\":71}\"}" - span: - lo: 49 - hi: 72 + destination: "{\"name\":\"r5\",\"span\":\"{\\\"lo\\\":69,\\\"hi\\\":71}\"}" + span: + lo: 49 + hi: 71 span: lo: 0 hi: 74 diff --git a/tests/expectations/parser/parser/statement/assembly_block_fail.out b/tests/expectations/parser/parser/statement/assembly_block_fail.out index e8255f06fc..870ab00d3b 100644 --- a/tests/expectations/parser/parser/statement/assembly_block_fail.out +++ b/tests/expectations/parser/parser/statement/assembly_block_fail.out @@ -6,6 +6,7 @@ outputs: - "Error [EPAR0370009]: unexpected string: expected 'ident', got ']'\n --> test:1:10\n |\n 1 | assembly{]\n | ^" - "Error [EPAR0370005]: expected { -- got '['\n --> test:1:9\n |\n 1 | assembly[]\n | ^" - "Error [EPAR0370005]: expected ; -- got ''\n --> test:1:11\n |\n 1 | assembli {}\n | ^" - - "Error [EPAR0370009]: unexpected string: expected 'ident', got '}'\n --> test:3:1\n |\n 3 | }\n | ^" + - "Error [EPAR0370005]: expected ; -- got '}'\n --> test:3:1\n |\n 3 | }\n | ^" - "Error [EPAR0370047]: Invalid opcode in assembly block.\n --> test:2:1\n |\n 2 | thisopcodedoesnotexist r1 r2 into r3;\n | ^^^^^^^^^^^^^^^^^^^^^^" - - "Error [EPAR0370048]: Invalid operand in assembly block. Operands must be `Identifier`s or `LiteralExpressions`.\n --> test:2:5\n |\n 2 | add r1 (1u8 + 2u8) into r3;\n | ^^^^^^^^^^^^^^" + - "Error [EPAR0370005]: expected `into` -- got 'thishouldbeinto'\n --> test:2:11\n |\n 2 | add r1 r2 thishouldbeinto r3;\n | ^^^^^^^^^^^^^^^" + - "Error [EPAR0370048]: Invalid operand in assembly block. Operands must be `Identifier`s or `LiteralExpressions`.\n --> test:2:9\n |\n 2 | add r1 (1u8 + 2u8) into r3;\n | ^^^^^^^^^" diff --git a/tests/parser/statement/assembly_block.leo b/tests/parser/statement/assembly_block.leo index bcbfc99ffd..b86700202e 100644 --- a/tests/parser/statement/assembly_block.leo +++ b/tests/parser/statement/assembly_block.leo @@ -11,9 +11,6 @@ assembly { add r1 r2 into r3; } -assembly { - add r1 r2 thisistreatedasanidentifier r3; -} assembly { add r1 r2 into r3; diff --git a/tests/parser/statement/assembly_block_fail.leo b/tests/parser/statement/assembly_block_fail.leo index d87194031b..ded0a38f95 100644 --- a/tests/parser/statement/assembly_block_fail.leo +++ b/tests/parser/statement/assembly_block_fail.leo @@ -19,6 +19,10 @@ assembly { thisopcodedoesnotexist r1 r2 into r3; } +assembly { + add r1 r2 thishouldbeinto r3; +} + assembly { add r1 (1u8 + 2u8) into r3; }