Skip to content

Commit

Permalink
Fix binary operations with function calls
Browse files Browse the repository at this point in the history
  • Loading branch information
garritfra committed Dec 12, 2024
1 parent eb09341 commit 4393563
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 27 deletions.
4 changes: 0 additions & 4 deletions src/parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,4 @@ impl Parser {
let new_lines = "\n".repeat(3);
format!("{new_lines}Hint: {}\n", msg)
}

pub(super) fn prev(&mut self) -> Option<Token> {
self.prev.clone()
}
}
33 changes: 10 additions & 23 deletions src/parser/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,10 +254,15 @@ impl Parser {
Expression::Variable(ident.clone())
};

// TODO: Use match statement
if self.peek_token(TokenKind::BraceOpen).is_ok() {
let state = self.parse_function_call(Some(ident))?;
Ok(Statement::Exp(state))
let fn_call = self.parse_function_call(Some(ident))?;
let next = self.peek()?;
if BinOp::try_from(next.kind.clone()).is_ok() {
let bin_op = self.parse_bin_op(Some(fn_call))?;
Ok(Statement::Exp(bin_op))
} else {
Ok(Statement::Exp(fn_call))
}
} else if self.peek_token(TokenKind::Assign).is_ok() {
let state = self.parse_assignent(Some(expr))?;
Ok(state)
Expand All @@ -270,8 +275,6 @@ impl Parser {
_ => Ok(Statement::Exp(expr)),
}
} else if BinOp::try_from(self.peek()?.kind).is_ok() {
// Parse Binary operation
let expr = Expression::Variable(ident);
let state = Statement::Exp(self.parse_bin_op(Some(expr))?);
Ok(state)
} else if self.peek_token(TokenKind::Dot).is_ok() {
Expand Down Expand Up @@ -333,6 +336,7 @@ impl Parser {
let expr = Expression::FunctionCall { fn_name, args };
match self.peek()?.kind {
TokenKind::Dot => self.parse_field_access(expr),
_ if BinOp::try_from(self.peek()?.kind).is_ok() => self.parse_bin_op(Some(expr)),
_ => Ok(expr),
}
}
Expand Down Expand Up @@ -406,7 +410,6 @@ impl Parser {
other => return Err(format!("Expected Expression, found {:?}", other)),
};

// Only try to peek if we have more tokens
if !self.has_more() {
return Ok(expr);
}
Expand Down Expand Up @@ -454,7 +457,6 @@ impl Parser {
}
}

/// TODO: Cleanup
fn parse_struct_initialization(&mut self) -> Result<Expression, String> {
let name = self.match_identifier()?;
self.match_token(TokenKind::CurlyBracesOpen)?;
Expand Down Expand Up @@ -670,25 +672,10 @@ impl Parser {
}
}

/// In some occurences a complex expression has been evaluated before a binary operation is encountered.
/// The following expression is one such example:
/// ```
/// foo(1) * 2
/// ```
/// In this case, the function call has already been evaluated, and needs to be passed to this function.
fn parse_bin_op(&mut self, lhs: Option<Expression>) -> Result<Expression, String> {
let left = match lhs {
Some(lhs) => lhs,
None => {
let prev = self.prev().ok_or("Expected token")?;
match &prev.kind {
TokenKind::Identifier(_) | TokenKind::Literal(_) | TokenKind::Keyword(_) => {
Ok(Expression::try_from(prev)?)
}
_ => Err(self
.make_error_msg(prev.pos, "Failed to parse binary operation".to_string())),
}?
}
None => self.parse_expression()?,
};

let op = self.match_operator()?;
Expand Down
164 changes: 164 additions & 0 deletions src/parser/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,170 @@ fn test_parse_compound_ops_with_function_call() {
assert!(tree.is_ok())
}

#[test]
fn test_parse_function_call_binary_op() {
let raw = "
fn main() {
foo(1) * 2
}
fn foo(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse 'foo(1) * 2': {:?}",
tree.err()
);
}

#[test]
fn test_parse_complex_function_call_binary_op() {
let raw = "
fn main() {
let x = foo(bar(1, 2)) * 3 + 4
}
fn foo(x: int) {
return x
}
fn bar(x: int, y: int) {
return x + y
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse nested function calls with binary ops: {:?}",
tree.err()
);
}

#[test]
fn test_parse_function_call_binary_op_in_return() {
let raw = "
fn main() {
return foo(1) * bar(2)
}
fn foo(x: int) {
return x
}
fn bar(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse binary op in return statement: {:?}",
tree.err()
);
}

#[test]
fn test_parse_function_call_binary_op_nested() {
let raw = "
fn main() {
let x = (foo(1) * 2) + (bar(3) * 4)
}
fn foo(x: int) {
return x
}
fn bar(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse nested binary operations: {:?}",
tree.err()
);
}

#[test]
fn test_parse_function_call_binary_op_with_variables() {
let raw = "
fn main() {
let a = 5
let result = foo(a) * 2
}
fn foo(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse binary op with variables: {:?}",
tree.err()
);
}

#[test]
fn test_parse_function_call_comparison_op() {
let raw = "
fn main() {
if foo(1) > bar(2) {
return true
}
return false
}
fn foo(x: int) {
return x
}
fn bar(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse comparison with function calls: {:?}",
tree.err()
);
}

#[test]
fn test_parse_function_call_binary_op_precedence() {
let raw = "
fn main() {
let result = foo(1) * 2 + bar(3) * 4
}
fn foo(x: int) {
return x
}
fn bar(x: int) {
return x
}
";
let tokens = tokenize(raw).unwrap();
let tree = parse(tokens, Some(raw.to_string()));
assert!(
tree.is_ok(),
"Failed to parse operator precedence with function calls: {:?}",
tree.err()
);
}

#[test]
fn test_parse_compound_ops_with_strings() {
let raw = "
Expand Down

0 comments on commit 4393563

Please sign in to comment.