wip -- still working on binary expressions
This commit is contained in:
@@ -84,6 +84,14 @@ fn stress_test_negation_with_stack_spillover() -> anyhow::Result<()> {
|
|||||||
compiled,
|
compiled,
|
||||||
indoc! {
|
indoc! {
|
||||||
"
|
"
|
||||||
|
j main
|
||||||
|
main:
|
||||||
|
add r1 -1 -2
|
||||||
|
add r2 -5 -6
|
||||||
|
mul r3 -4 r2
|
||||||
|
add r4 -3 r3
|
||||||
|
mul r5 r1 r4
|
||||||
|
move r8 r5 #negationHell
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -747,3 +747,4 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,33 @@ impl Parser {
|
|||||||
Ok(self.current_token.as_ref())
|
Ok(self.current_token.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses an expression, handling binary operations with correct precedence.
|
||||||
fn expression(&mut self) -> Result<Option<tree_node::Expression>, Error> {
|
fn expression(&mut self) -> Result<Option<tree_node::Expression>, Error> {
|
||||||
|
// Parse the Left Hand Side (unary/primary expression)
|
||||||
|
let lhs = self.unary()?;
|
||||||
|
|
||||||
|
let Some(lhs) = lhs else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if the next or current token is an operator
|
||||||
|
if self_matches_peek!(self, TokenType::Symbol(s) if s.is_operator()) {
|
||||||
|
return Ok(Some(Expression::Binary(self.binary(lhs)?)));
|
||||||
|
}
|
||||||
|
// This is an edge case. We need to move back one token if the current token is an operator
|
||||||
|
// so the binary expression can pick up the operator
|
||||||
|
else if self_matches_current!(self, TokenType::Symbol(s) if s.is_operator()) {
|
||||||
|
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
||||||
|
return Ok(Some(Expression::Binary(self.binary(lhs)?)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(lhs))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses a unary or primary expression.
|
||||||
|
/// This handles prefix operators (like negation) and atomic expressions (literals, variables, etc.),
|
||||||
|
/// but stops before consuming binary operators.
|
||||||
|
fn unary(&mut self) -> Result<Option<tree_node::Expression>, Error> {
|
||||||
macro_rules! matches_keyword {
|
macro_rules! matches_keyword {
|
||||||
($keyword:expr, $($pattern:pat),+) => {
|
($keyword:expr, $($pattern:pat),+) => {
|
||||||
matches!($keyword, $($pattern)|+)
|
matches!($keyword, $($pattern)|+)
|
||||||
@@ -167,7 +193,7 @@ impl Parser {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expr = Some(match current_token.token_type {
|
let expr = match current_token.token_type {
|
||||||
// match unsupported keywords
|
// match unsupported keywords
|
||||||
TokenType::Keyword(e)
|
TokenType::Keyword(e)
|
||||||
if matches_keyword!(e, Keyword::Enum, Keyword::If, Keyword::Else) =>
|
if matches_keyword!(e, Keyword::Enum, Keyword::If, Keyword::Else) =>
|
||||||
@@ -217,7 +243,10 @@ impl Parser {
|
|||||||
// match minus symbols to handle negative numbers or negated expressions
|
// match minus symbols to handle negative numbers or negated expressions
|
||||||
TokenType::Symbol(Symbol::Minus) => {
|
TokenType::Symbol(Symbol::Minus) => {
|
||||||
self.assign_next()?; // consume the `-` symbol
|
self.assign_next()?; // consume the `-` symbol
|
||||||
let inner_expr = self.expression()?.ok_or(Error::UnexpectedEOF)?;
|
// IMPORTANT: We call `unary()` here, NOT `expression()`.
|
||||||
|
// This ensures negation binds tightly to the operand and doesn't consume binary ops.
|
||||||
|
// e.g. `-1 + 2` parses as `(-1) + 2`
|
||||||
|
let inner_expr = self.unary()?.ok_or(Error::UnexpectedEOF)?;
|
||||||
|
|
||||||
Expression::Negation(boxed!(inner_expr))
|
Expression::Negation(boxed!(inner_expr))
|
||||||
}
|
}
|
||||||
@@ -225,23 +254,8 @@ impl Parser {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(Error::UnexpectedToken(current_token.clone()));
|
return Err(Error::UnexpectedToken(current_token.clone()));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
let Some(expr) = expr else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if the next or current token is an operator
|
|
||||||
if self_matches_peek!(self, TokenType::Symbol(s) if s.is_operator()) {
|
|
||||||
return Ok(Some(Expression::Binary(self.binary(expr)?)));
|
|
||||||
}
|
|
||||||
// This is an edge case. We need to move back one token if the current token is an operator
|
|
||||||
// so the binary expression can pick up the operator
|
|
||||||
else if self_matches_current!(self, TokenType::Symbol(s) if s.is_operator()) {
|
|
||||||
self.tokenizer.seek(SeekFrom::Current(-1))?;
|
|
||||||
return Ok(Some(Expression::Binary(self.binary(expr)?)));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Some(expr))
|
Ok(Some(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,17 +273,19 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
// A priority expression ( -> (1 + 2) <- + 3 )
|
// A priority expression ( -> (1 + 2) <- + 3 )
|
||||||
TokenType::Symbol(Symbol::LParen) => self.priority().map(Expression::Priority),
|
TokenType::Symbol(Symbol::LParen) => self.priority().map(Expression::Priority),
|
||||||
TokenType::Symbol(Symbol::Minus) => {
|
|
||||||
self.assign_next()?;
|
|
||||||
let inner = self.get_binary_child_node()?;
|
|
||||||
Ok(Expression::Negation(boxed!(inner)))
|
|
||||||
}
|
|
||||||
// A function invocation
|
// A function invocation
|
||||||
TokenType::Identifier(_)
|
TokenType::Identifier(_)
|
||||||
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
|
||||||
{
|
{
|
||||||
self.invocation().map(Expression::Invocation)
|
self.invocation().map(Expression::Invocation)
|
||||||
}
|
}
|
||||||
|
// Handle Negation
|
||||||
|
TokenType::Symbol(Symbol::Minus) => {
|
||||||
|
self.assign_next()?;
|
||||||
|
// recurse to handle double negation or simple negation of atoms
|
||||||
|
let inner = self.get_binary_child_node()?;
|
||||||
|
Ok(Expression::Negation(boxed!(inner)))
|
||||||
|
}
|
||||||
_ => Err(Error::UnexpectedToken(current_token.clone())),
|
_ => Err(Error::UnexpectedToken(current_token.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user