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

View File

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

View File

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