Clean up AssemblyBlock parser, fix errors

This commit is contained in:
Pranav Gaddamadugu 2022-06-30 19:04:23 -07:00
parent 73c761149f
commit 1036113be9
8 changed files with 94 additions and 84 deletions

View File

@ -498,7 +498,7 @@ impl ParserContext<'_> {
/// - self
///
/// Returns an expression error if the token cannot be matched.
fn parse_primary_expression(&mut self) -> Result<Expression> {
pub fn parse_primary_expression(&mut self) -> Result<Expression> {
if let Token::LeftParen = self.token.token {
return self.parse_tuple_expression();
}

View File

@ -14,8 +14,9 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
// 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<Instruction> {
// 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<Operand> {
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)),

View File

@ -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,
}
);

View File

@ -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.

View File

@ -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

View File

@ -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 '<eof>'\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 | ^^^^^^^^^"

View File

@ -11,9 +11,6 @@ assembly {
add r1 r2 into r3;
}
assembly {
add r1 r2 thisistreatedasanidentifier r3;
}
assembly {
add r1 r2 into r3;

View File

@ -19,6 +19,10 @@ assembly {
thisopcodedoesnotexist r1 r2 into r3;
}
assembly {
add r1 r2 thishouldbeinto r3;
}
assembly {
add r1 (1u8 + 2u8) into r3;
}