wip -- still working on binary expressions

This commit is contained in:
2025-11-24 22:13:49 -07:00
parent ae8199fa8c
commit 56f0e292b7
3 changed files with 47 additions and 22 deletions

View File

@@ -84,6 +84,14 @@ fn stress_test_negation_with_stack_spillover() -> anyhow::Result<()> {
compiled,
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
"
}
);

View File

@@ -747,3 +747,4 @@ impl<'a, W: std::io::Write> Compiler<'a, W> {
Ok(())
}
}

View File

@@ -152,7 +152,33 @@ impl Parser {
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> {
// 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 {
($keyword:expr, $($pattern:pat),+) => {
matches!($keyword, $($pattern)|+)
@@ -167,7 +193,7 @@ impl Parser {
return Ok(None);
}
let expr = Some(match current_token.token_type {
let expr = match current_token.token_type {
// match unsupported keywords
TokenType::Keyword(e)
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
TokenType::Symbol(Symbol::Minus) => {
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))
}
@@ -225,23 +254,8 @@ impl Parser {
_ => {
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))
}
@@ -259,17 +273,19 @@ impl Parser {
}
// A priority expression ( -> (1 + 2) <- + 3 )
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
TokenType::Identifier(_)
if self_matches_peek!(self, TokenType::Symbol(Symbol::LParen)) =>
{
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())),
}
}