Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unary operator precedence & complete operator support in LLVM #21

Merged
merged 5 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 54 additions & 11 deletions src/code_generation/llvm/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,34 @@ impl<'ctx> LLVMGenerator<'ctx> {
self.generate_unary_expression(operator, operand)
}
Expression::Assignment(lhs, rhs) => self.generate_assignment(lhs, rhs),
_ => todo!(),
Expression::Parenthesized(expression) => self.generate_expression(expression),
Expression::FunctionCall(func_name, args) => {
self.generate_function_call(&func_name.value, args)
}
Expression::Empty => self
.context
.i32_type()
.const_int(0, false)
.as_basic_value_enum(),
}
}

fn generate_function_call(
&mut self,
name: &str,
args: &Vec<Expression>,
) -> BasicValueEnum<'ctx> {
let function = self.module.get_function(name).unwrap();
let mut arguments = Vec::new();
for arg in args {
arguments.push(self.generate_expression(arg).into());
}
self.builder
.build_call(function, &arguments, "call")
.unwrap()
.try_as_basic_value()
.left()
.unwrap()
}

fn generate_unary_expression(
Expand Down Expand Up @@ -834,16 +860,28 @@ mod tests {
let ast = syntax_analysis::parser::Parser::new(tokens).parse();
let generated_ir =
code_generation::llvm::generator::LLVMGenerator::new(&mut context).generate(&ast);
let exit_code = interpret_llvm_ir(&generated_ir);
assert_eq!(
test_case.expected % 256,
exit_code % 256,
"Test case: {} -- Expected: {}, found: {}\nGenerated IR:\n{}",
test_case.name,
test_case.expected,
exit_code,
generated_ir
);
let (exit_code, stdout_str) = interpret_llvm_ir(&generated_ir);
if test_case.expected_exit_code.is_some() {
let expected_exit_code = test_case.expected_exit_code.unwrap();
assert_eq!(
(expected_exit_code + 256) % 256,
(exit_code + 256) % 256,
"Test case: {} -- Expected exit code: {}, found: {}\nGenerated IR:\n{}",
test_case.name,
expected_exit_code,
exit_code,
generated_ir
);
}

if test_case.expected_output.is_some() {
let expected_output = test_case.expected_output.unwrap();
assert_eq!(
expected_output, stdout_str,
"Test case: {} -- Expected stdout: {}, found: {}\nGenerated IR:\n{}",
test_case.name, expected_output, stdout_str, generated_ir
);
}
}
}

Expand Down Expand Up @@ -887,4 +925,9 @@ mod tests {
fn test_for() {
run_tests_from_file("./src/tests/for.c");
}

#[test]
fn test_function_calls() {
run_tests_from_file("./src/tests/function_calls.c");
}
}
58 changes: 51 additions & 7 deletions src/syntax_analysis/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,11 @@ fn binary_operator_precedence(token_type: TokenType) -> u8 {
}
}

fn is_unary_operator(token_type: &TokenType) -> bool {
*token_type == TokenType::Plus || *token_type == TokenType::Minus
fn unary_operator_precedence(token_type: &TokenType) -> u8 {
match token_type {
TokenType::Plus | TokenType::Minus | TokenType::Bang => 10,
_ => 0,
}
}

impl Parser {
Expand Down Expand Up @@ -240,10 +243,16 @@ impl Parser {
if self.is_assignment() {
return self.parse_assignment_expression();
}
if is_unary_operator(&self.current().token_type) {
return Expression::Unary(self.consume().clone(), Box::new(self.parse_expression()));
let mut left = None;
let unary_op_precedence = unary_operator_precedence(&self.current().token_type);
if unary_op_precedence != 0 && unary_op_precedence >= parent_precedence {
left = Some(Expression::Unary(
self.consume().clone(),
Box::new(self.parse_expression_internal(unary_op_precedence)),
));
} else {
left = Some(self.parse_primary_expression());
}
let mut left = self.parse_primary_expression();
loop {
let operator_token = self.current().clone();
let operator_precedence = binary_operator_precedence(operator_token.token_type.clone());
Expand All @@ -255,9 +264,13 @@ impl Parser {
}
self.advance();
let right = self.parse_expression_internal(operator_precedence);
left = Expression::Binary(operator_token, Box::new(left), Box::new(right))
left = Some(Expression::Binary(
operator_token,
Box::new(left.unwrap()),
Box::new(right),
))
}
left
left.unwrap()
}

fn parse_parenthesized_expression(&mut self) -> Expression {
Expand Down Expand Up @@ -690,6 +703,37 @@ mod tests {
)
)
]
#[case("return -2 + 1;",
TranslationUnit(
vec![
ReturnStatement(
Token{value: "return".to_string(), token_type: TokenType::Return, pos: 0},
Box::new(
ExpressionNode(
Expression::Binary(
Token{value: "+".to_string(), token_type: TokenType::Plus, pos: 10},
Box::new(
Expression::Unary(
Token{value: "-".to_string(), token_type: TokenType::Minus, pos: 7},
Box::new(
Expression::IntegerLiteral(
Token{value: "2".to_string(), token_type: TokenType::IntegerLiteral, pos: 8}
)
)
)
),
Box::new(
Expression::IntegerLiteral(
Token{value: "1".to_string(), token_type: TokenType::IntegerLiteral, pos: 12}
)
)
)
)
)
)
]
)
)]
fn test_parse_unary_expression(#[case] test_case: String, #[case] expected: ASTNode) {
let tokens = Lexer::new(test_case).lex();
let result = Parser::new(tokens).parse();
Expand Down
37 changes: 37 additions & 0 deletions src/tests/function_calls.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// CASE Basic function call
// RETURNS 1

int f(int x) {
return x - 1;
}

int main() {
return f(2);
}

// CASE Recursive Fibonacci
// RETURNS 3

int fib(int n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}

int main() {
return fib(4);
}

// CASE Print to stdout
// RETURNS 0
// Outputs Hello

int putchar(int c);

int main() {
putchar(72);
putchar(101);
putchar(108);
putchar(108);
putchar(111);
return 0;
}
67 changes: 40 additions & 27 deletions src/tests/operators.c
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
// Case Binary operator +
// Returns 3
// CASE Binary operator +
// RETURNS 3

int main() {
return 1 + 2;
}

// Case Binary operator -
// Returns -1
// CASE Binary operator -
// RETURNS -1

int main() {
return 1 - 2;
}

// Case Binary operator *
// Returns 10
// CASE Binary operator *
// RETURNS 10

int main() {
return 5 * 2;
}

// Case Binary operator *
// Returns 4
// CASE Binary operator *
// RETURNS 4

int main() {
return 9 / 2;
}

// Case Binary arithmetic operators combined
// Returns 9697
// CASE Binary arithmetic operators combined
// RETURNS 9697

int main() {
int x = 312;
Expand All @@ -37,43 +37,56 @@ int main() {
return z - x;
}

// Case Binary operator ||
// Returns 1
// CASE Binary operator ||
// RETURNS 1

int main() {
int false = 0; int true = 123; return true || false;
int false = 0; int true = 123; int y = true || false; return y;
}

// Case Binary operator &&
// Returns 0
// CASE Binary operator &&
// RETURNS 0

int main() {
int false = 0; int true = 123; return true && false;
int false = 0; int true = 123; int y = true && false; return y;
}

// Case Binary operator ^
// Returns 123
// CASE Binary operator ^
// RETURNS 123

int main() {
int false = 0; int true = 123; return true ^ false;
}

// Case Unary operator ! 1
// Returns 1
// CASE Unary operator ! 1
// RETURNS 1

int main() {
return !0;
int x = !0;
return x;
}

// Case Unary operator ! 2
// Returns 0
// CASE Unary operator ! 2
// RETURNS 0

int main() {
return !1;
int x = !1;
return x;
}

// Case Unary +-
// Returns 144
// CASE Unary +-
// RETURNS 144

int main() {
int x = -----++++----+------12; return x * x;
}
}

// CASE Binary, unary and parenthesized expressions
// RETURNS -1

int main() {
int x = 3;
int y = 4;
int z = 5;
return (-x + y) * (z - x) / (-y + z) - x;
}
Loading