From 91be3e02fbd7d1cb7487cfdf28dd383cfc14f710 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Fri, 27 Oct 2023 23:41:42 +0200 Subject: [PATCH 1/7] LLVM: generate if statemets --- src/code_generation/llvm/generator.rs | 99 ++++++++++++++++++++++++--- src/tests/if.c | 45 ++++++++++++ 2 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 src/tests/if.c diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index 2130dd6..20f5384 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -17,6 +17,8 @@ pub struct LLVMGenerator<'ctx> { builder: Builder<'ctx>, module: Module<'ctx>, symbol_table: SymbolTable<'ctx>, + current_function: Option>, // The function currently being generated + counter: i32, // For naming labels of blocks } impl<'ctx> LLVMGenerator<'ctx> { @@ -30,6 +32,8 @@ impl<'ctx> LLVMGenerator<'ctx> { builder, module, symbol_table, + current_function: None, + counter: 0, } } } @@ -51,8 +55,8 @@ impl<'ctx> LLVMGenerator<'ctx> { FunctionDeclaration(..) => self.generate_function_declaration(node).as_any_value_enum(), FunctionDefinition(..) => self.generate_function_definition(node).as_any_value_enum(), ExpressionNode(expression) => self.generate_expression(expression).as_any_value_enum(), - // Scope(..) => self.generate_scope(node), - // If(..) => self.generate_if_statement(node), + Scope(..) => self.generate_scope(node).as_any_value_enum(), + If(..) => self.generate_if_statement(node).as_any_value_enum(), // While(..) => self.generate_while(node), // DoWhile(..) => self.generate_do_while(node), // ExpressionStatement(..) => self.generate_expression_statement(node), @@ -280,7 +284,6 @@ impl<'ctx> LLVMGenerator<'ctx> { ), } } - // TODO add the function to the symbol table. function } _ => panic!( @@ -299,6 +302,8 @@ impl<'ctx> LLVMGenerator<'ctx> { params.clone(), )); + self.current_function = Some(declaration); + self.symbol_table.push_scope(); let basic_block = self.context.append_basic_block(declaration, "entry"); @@ -422,13 +427,84 @@ impl<'ctx> LLVMGenerator<'ctx> { _ => panic!(), } } + fn generate_scope(&mut self, node: &ASTNode) -> IntValue<'ctx> { + self.symbol_table.push_scope(); - fn generate_scope(&mut self, scope: &ASTNode) -> String { - todo!() + let statements = match node { + Scope(statements) => statements, + _ => panic!( + "Internal error: expected translation unit, found: {:?}", + node + ), + }; + + for statement in statements { + self.generate(statement); + } + + self.symbol_table.pop_scope(); + + self.context.i32_type().const_int(0, false) } - fn generate_if_statement(&mut self, node: &ASTNode) -> String { - todo!() + fn generate_if_statement(&mut self, node: &ASTNode) -> IntValue<'ctx> { + let (condition_node, then_node, else_node) = match node { + If(_, condition_node, then_node, else_node) => (condition_node, then_node, else_node), + _ => panic!(), + }; + + let counter = self.counter; + + let then_block = self + .context + .append_basic_block(self.current_function.unwrap(), &format!("then_{counter}")); + let end_block = self + .context + .append_basic_block(self.current_function.unwrap(), &format!("if_end_{counter}")); + let else_block = if else_node.is_some() { + self.context + .append_basic_block(self.current_function.unwrap(), &format!("else_{counter}")) + } else { + end_block + }; + + self.counter += 1; + + let condition = match condition_node.as_ref() { + ASTNode::ExpressionNode(expression) => expression, + _ => panic!(), + }; + + let cond_result = self.generate_expression(condition); + + let zero = self.context.i32_type().const_int(0, false); + let bool_value = self + .builder + .build_int_compare( + inkwell::IntPredicate::NE, + cond_result.into_int_value(), + zero, + "bool_value", + ) + .unwrap(); + self.builder + .build_conditional_branch(bool_value, then_block, else_block) + .unwrap(); + + self.builder.position_at_end(then_block); + + self.generate(then_node); + self.builder.build_unconditional_branch(end_block).unwrap(); + + if else_node.is_some() { + self.builder.position_at_end(else_block); + self.generate(else_node.as_ref().unwrap()); + self.builder.build_unconditional_branch(end_block).unwrap(); + } + + self.builder.position_at_end(end_block); + + zero } fn generate_while(&mut self, while_node: &ASTNode) -> String { @@ -473,8 +549,8 @@ mod tests { let exit_code = interpret_llvm_ir(&generated_ir); assert_eq!( test_case.expected, exit_code, - "Test case: {} -- Expected: {}, found: {}", - test_case.name, test_case.expected, exit_code + "Test case: {} -- Expected: {}, found: {}\nGenerated IR:\n{}", + test_case.name, test_case.expected, exit_code, generated_ir ); } } @@ -489,4 +565,9 @@ mod tests { fn test_erroneous_variable_declarations_and_definitions() { run_tests_from_file("./src/tests/variables_error.c"); } + + #[test] + fn test_basic_if() { + run_tests_from_file("./src/tests/if.c"); + } } diff --git a/src/tests/if.c b/src/tests/if.c new file mode 100644 index 0000000..f312dc6 --- /dev/null +++ b/src/tests/if.c @@ -0,0 +1,45 @@ +// CASE Basic if 1 +// RETURNS 5 + +int main() { + if (1) { return 5; } + else {return 6; } + return 0; +} + +// CASE Basic if 2 +// RETURNS 5 + +int main() { + if (1) { return 5; } + return 6; +} + +// CASE if, else if +// RETURNS 5 + +int main() { + if (0) + return 6; + else if (1) + return 5; + return 7; +} + +// CASE nested if, else if +// RETURNS 11 + +int main() { + if (0) + return 6; + else if (1) { + if (0) + return 15; + else if (1) { + return 11; + } else { + return 17; + } + } + return 7; +} From 6b9d504fee5a9316e747a9a3d6545a52023599a9 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sat, 28 Oct 2023 00:48:25 +0200 Subject: [PATCH 2/7] LLVM: Support conditional binary operators --- src/code_generation/llvm/generator.rs | 19 ++++++++++++++++++- src/tests/if.c | 14 ++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index 20f5384..2d9fe15 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -391,10 +391,26 @@ impl<'ctx> LLVMGenerator<'ctx> { match expression { Expression::IntegerLiteral { .. } => self.generate_integer_literal(expression), Expression::Variable(name) => self.generate_variable_expression(name), + Expression::Binary(op, lhs, rhs) => self.generate_binary_expression(op, lhs, rhs), _ => todo!(), } } + fn generate_binary_expression(&mut self, token: &Token, lhs: &Expression, rhs: &Expression) -> BasicValueEnum<'ctx> { + let lhs = self.generate_expression(lhs).into_int_value(); + let rhs = self.generate_expression(rhs).into_int_value(); + // TODO implement the rest of the binary expressions + match token.token_type { + TokenType::EqualsEquals => self.builder.build_int_compare(inkwell::IntPredicate::EQ, lhs, rhs, "bool_value"), + TokenType::NotEquals => self.builder.build_int_compare(inkwell::IntPredicate::NE, lhs, rhs, "bool_value"), + TokenType::GreaterThan => self.builder.build_int_compare(inkwell::IntPredicate::SGT, lhs, rhs, "bool_value"), + TokenType::GreaterThanEquals => self.builder.build_int_compare(inkwell::IntPredicate::SGE, lhs, rhs, "bool_value"), + TokenType::LessThan => self.builder.build_int_compare(inkwell::IntPredicate::SLT, lhs, rhs, "bool_value"), + TokenType::LessThanEquals => self.builder.build_int_compare(inkwell::IntPredicate::SLE, lhs, rhs, "bool_value"), + _ => panic!() + }.unwrap().as_basic_value_enum() + } + fn generate_variable_expression(&mut self, name: &Token) -> BasicValueEnum<'ctx> { if let Some(variable) = self.symbol_table.find(&name.value) { self.builder @@ -478,11 +494,12 @@ impl<'ctx> LLVMGenerator<'ctx> { let cond_result = self.generate_expression(condition); let zero = self.context.i32_type().const_int(0, false); + let i32_value = self.builder.build_int_z_extend(cond_result.into_int_value(), self.context.i32_type(), "extended_condition").unwrap(); let bool_value = self .builder .build_int_compare( inkwell::IntPredicate::NE, - cond_result.into_int_value(), + i32_value, zero, "bool_value", ) diff --git a/src/tests/if.c b/src/tests/if.c index f312dc6..ddc85a5 100644 --- a/src/tests/if.c +++ b/src/tests/if.c @@ -19,9 +19,10 @@ int main() { // RETURNS 5 int main() { - if (0) + int x = 5; + if (x < 3) return 6; - else if (1) + else if (x >= 5) return 5; return 7; } @@ -30,12 +31,13 @@ int main() { // RETURNS 11 int main() { - if (0) + int y = 54; + if (y < 22) return 6; - else if (1) { - if (0) + else if (y > 20) { + if (y < 30) return 15; - else if (1) { + else if (y > 30) { return 11; } else { return 17; From eeaed9c239118e35921d87199b96df8385fb1fce Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 29 Oct 2023 00:07:32 +0200 Subject: [PATCH 3/7] Support parsing assignment as an expression (which allows for chain assignment) --- src/syntax_analysis/parser.rs | 51 ++++++++++++++++++++++++++++++----- src/tests/assignment.c | 31 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 src/tests/assignment.c diff --git a/src/syntax_analysis/parser.rs b/src/syntax_analysis/parser.rs index 8546ade..c672376 100644 --- a/src/syntax_analysis/parser.rs +++ b/src/syntax_analysis/parser.rs @@ -1,9 +1,12 @@ use crate::lexical_analysis::tokens::*; use crate::syntax_analysis::ast::ASTNode::*; use crate::syntax_analysis::ast::*; +use crate::utils::test_utils::interpret_llvm_ir; +use std::panic::panic_any; pub struct Parser { tokens: Vec, + terminator_stack: Vec, pos: usize, } @@ -31,7 +34,11 @@ fn is_unary_operator(token_type: &TokenType) -> bool { impl Parser { pub fn new(tokens: Vec) -> Self { - Self { tokens, pos: 0 } + Self { + tokens, + pos: 0, + terminator_stack: vec![], + } } pub fn parse_unit(&mut self) -> ASTNode { @@ -57,6 +64,7 @@ impl Parser { fn parse_scope(&mut self) -> ASTNode { let mut result = Vec::new(); + self.push_terminator(TokenType::CloseCurly); self.try_consume(TokenType::OpenCurly); while self.current().token_type != TokenType::CloseCurly && self.current().token_type != TokenType::Eof @@ -64,6 +72,7 @@ impl Parser { result.push(self.parse_unit()); } self.try_consume(TokenType::CloseCurly); + self.pop_terminator(TokenType::CloseCurly); Scope(result) } @@ -106,7 +115,10 @@ impl Parser { if next.token_type == TokenType::Equals { return true; } - if next.token_type == TokenType::SemiColon || next.token_type == TokenType::Eof { + if next.token_type == TokenType::SemiColon + || next.token_type == TokenType::Eof + || self.is_next_terminator(&next.token_type) + { break; } i += 1; @@ -141,6 +153,7 @@ impl Parser { fn parse_for(&mut self) -> ASTNode { let for_token = self.try_consume(TokenType::For); + self.push_terminator(TokenType::CloseParen); self.try_consume(TokenType::OpenParen); let init = self.parse_statement(); @@ -152,6 +165,7 @@ impl Parser { } self.try_consume(TokenType::CloseParen); + self.pop_terminator(TokenType::CloseParen); let body = self.parse_scope_or_single_statement(); @@ -223,6 +237,9 @@ impl Parser { } fn parse_expression_internal(&mut self, parent_precedence: u8) -> Expression { + 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())); } @@ -245,14 +262,17 @@ impl Parser { fn parse_parenthesized_expression(&mut self) -> Expression { self.try_consume(TokenType::OpenParen); + self.push_terminator(TokenType::CloseParen); let expr = self.parse_expression(); self.try_consume(TokenType::CloseParen); + self.pop_terminator(TokenType::CloseParen); expr } fn parse_function_arguments(&mut self) -> Vec { let mut result = Vec::new(); self.try_consume(TokenType::OpenParen); + self.push_terminator(TokenType::CloseParen); while self.current().token_type != TokenType::CloseParen && self.current().token_type != TokenType::Eof { @@ -262,9 +282,24 @@ impl Parser { } } self.try_consume(TokenType::CloseParen); + self.pop_terminator(TokenType::CloseParen); result } + fn push_terminator(&mut self, terminator: TokenType) { + self.terminator_stack.push(terminator); + } + + fn pop_terminator(&mut self, terminator: TokenType) { + if self.terminator_stack.pop().unwrap() != terminator { + panic!(); + } + } + + fn is_next_terminator(&self, token_type: &TokenType) -> bool { + !self.terminator_stack.is_empty() && token_type == self.terminator_stack.last().unwrap() + } + fn parse_function_call(&mut self) -> Expression { let identifier = self.try_consume(TokenType::Identifier); let arguments = self.parse_function_arguments(); @@ -288,14 +323,15 @@ impl Parser { } } - fn parse_assignment_no_semicolon(&mut self) -> ASTNode { + fn parse_assignment_expression(&mut self) -> Expression { let identifier_token = self.try_consume(TokenType::Identifier); self.try_consume(TokenType::Equals); let expression = self.parse_expression(); - ASTNode::ExpressionStatement(Expression::Assignment( - identifier_token, - Box::new(expression), - )) + Expression::Assignment(identifier_token, Box::new(expression)) + } + + fn parse_assignment_no_semicolon(&mut self) -> ASTNode { + ASTNode::ExpressionStatement(self.parse_assignment_expression()) } fn parse_assignment(&mut self) -> ASTNode { let assignment_expression = self.parse_assignment_no_semicolon(); @@ -519,6 +555,7 @@ mod tests { fn test_parse_binary_expression(#[case] test_case: String, #[case] expected: ASTNode) { let tokens = Lexer::new(test_case).lex(); let result = Parser::new(tokens).parse(); + println!("{:#?}", result); assert_eq!(expected, result); } diff --git a/src/tests/assignment.c b/src/tests/assignment.c new file mode 100644 index 0000000..cc64723 --- /dev/null +++ b/src/tests/assignment.c @@ -0,0 +1,31 @@ +<<<<<<< Updated upstream +======= +// CASE Basic assignment +// RETURNS 30 + +int main() { + int x = 5; + int y = 6; + return x * y; +} + +// CASE Chain assignment +// RETURNS 25 + +int main() { + int x = 0; + int y = x = 5; + return x * y; +} + +// CASE Global variable assignment +// RETURNS 6 + +int x = 5; + +int main() { + x = 6; + return x; +} + +>>>>>>> Stashed changes From cd4e3ec81975fc0f8dfd16d01d7a8c6923815a1d Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 29 Oct 2023 00:25:21 +0200 Subject: [PATCH 4/7] Support expression statements --- src/code_generation/llvm/generator.rs | 107 +++++++++++++++++++++-- src/code_generation/llvm/symbol_table.rs | 8 ++ src/tests/assignment.c | 4 - 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index 2d9fe15..f6dc764 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -59,7 +59,7 @@ impl<'ctx> LLVMGenerator<'ctx> { If(..) => self.generate_if_statement(node).as_any_value_enum(), // While(..) => self.generate_while(node), // DoWhile(..) => self.generate_do_while(node), - // ExpressionStatement(..) => self.generate_expression_statement(node), + ExpressionStatement(..) => self.generate_expression_statement(node).as_any_value_enum(), // For(..) => self.generate_for(node), _ => panic!(), } @@ -392,11 +392,41 @@ impl<'ctx> LLVMGenerator<'ctx> { Expression::IntegerLiteral { .. } => self.generate_integer_literal(expression), Expression::Variable(name) => self.generate_variable_expression(name), Expression::Binary(op, lhs, rhs) => self.generate_binary_expression(op, lhs, rhs), + Expression::Assignment(lhs, rhs) => self.generate_assignment(lhs, rhs), _ => todo!(), } } - fn generate_binary_expression(&mut self, token: &Token, lhs: &Expression, rhs: &Expression) -> BasicValueEnum<'ctx> { + fn generate_assignment(&mut self, lhs: &Token, rhs: &Expression) -> BasicValueEnum<'ctx> { + if self.is_in_global_scope() { + panic!("Assignment to variable {} not allowed in global scope", lhs.value); + } + if self.symbol_table.find_hierarchically(&lhs.value).is_none() { + panic!("Reference to undefined variable `{}`", lhs.value); + } + + let rhs = self.generate_expression(&rhs); + + // FIXME make sure that assignments to global variables are correct + self.builder + .build_store( + self.symbol_table + .find_hierarchically(&lhs.value) + .unwrap() + .pointer, + rhs, + ) + .unwrap(); + + rhs.clone() + } + + fn generate_binary_expression( + &mut self, + token: &Token, + lhs: &Expression, + rhs: &Expression, + ) -> BasicValueEnum<'ctx> { let lhs = self.generate_expression(lhs).into_int_value(); let rhs = self.generate_expression(rhs).into_int_value(); // TODO implement the rest of the binary expressions @@ -524,16 +554,76 @@ impl<'ctx> LLVMGenerator<'ctx> { zero } - fn generate_while(&mut self, while_node: &ASTNode) -> String { - todo!() + fn generate_while(&mut self, node: &ASTNode) -> IntValue<'ctx> { + let counter = self.counter; + let cond_block = self.context.append_basic_block( + self.current_function.unwrap(), + &format!("while_condition_{counter}"), + ); + let body_block = self.context.append_basic_block( + self.current_function.unwrap(), + &format!("while_body_{counter}"), + ); + let end_block = self.context.append_basic_block( + self.current_function.unwrap(), + &format!("while_end_{counter}"), + ); + + self.counter += 1; + + self.builder.build_unconditional_branch(cond_block).unwrap(); + + self.builder.position_at_end(cond_block); + + let (condition_node, body_node) = match node { + While(_, condition_node, body_node) => (condition_node, body_node), + _ => panic!(), + }; + + let condition = match condition_node.as_ref() { + ASTNode::ExpressionNode(expression) => expression, + _ => panic!(), + }; + + let cond_result = self.generate_expression(condition); + let i32_value = self + .builder + .build_int_z_extend( + cond_result.into_int_value(), + self.context.i32_type(), + "extended_condition", + ) + .unwrap(); + let zero = self.context.i32_type().const_int(0, false); + let bool_value = self + .builder + .build_int_compare(inkwell::IntPredicate::NE, i32_value, zero, "bool_value") + .unwrap(); + + self.builder + .build_conditional_branch(bool_value, body_block, end_block) + .unwrap(); + + self.builder.position_at_end(body_block); + + self.generate(body_node); + self.builder.build_unconditional_branch(cond_block).unwrap(); + + self.builder.position_at_end(end_block); + + self.context.i32_type().const_int(0, false) } fn generate_do_while(&mut self, node: &ASTNode) -> String { todo!() } - fn generate_expression_statement(&mut self, node: &ASTNode) -> String { - todo!() + fn generate_expression_statement(&mut self, node: &ASTNode) -> BasicValueEnum<'ctx> { + let expression = match node { + ExpressionStatement(expression) => expression, + _ => panic!(), + }; + return self.generate_expression(expression); } fn generate_for(&mut self, node: &ASTNode) -> String { @@ -587,4 +677,9 @@ mod tests { fn test_basic_if() { run_tests_from_file("./src/tests/if.c"); } + + #[test] + fn test_assignment() { + run_tests_from_file("./src/tests/assignment.c"); + } } diff --git a/src/code_generation/llvm/symbol_table.rs b/src/code_generation/llvm/symbol_table.rs index 9ccdc5d..fedc735 100644 --- a/src/code_generation/llvm/symbol_table.rs +++ b/src/code_generation/llvm/symbol_table.rs @@ -49,6 +49,14 @@ impl<'ctx> SymbolTable<'ctx> { None } + pub fn find_hierarchically(&self, name: &str) -> Option<&Variable<'ctx>> { + let mut result = self.find(&name); + if result.is_none() { + result = self.find_in_global_scope(&name); + } + result + } + pub fn find_in_current_scope(&self, name: &str) -> Option<&Variable<'ctx>> { if let Some(scope) = self.scopes.last() { return scope.get(name); diff --git a/src/tests/assignment.c b/src/tests/assignment.c index cc64723..ddafd0b 100644 --- a/src/tests/assignment.c +++ b/src/tests/assignment.c @@ -1,5 +1,3 @@ -<<<<<<< Updated upstream -======= // CASE Basic assignment // RETURNS 30 @@ -27,5 +25,3 @@ int main() { x = 6; return x; } - ->>>>>>> Stashed changes From 5b3a19394a849f964db9e5a37aa4e10e5914e7ad Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 29 Oct 2023 00:26:49 +0200 Subject: [PATCH 5/7] Support most of the binary operators --- src/code_generation/llvm/generator.rs | 57 +++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index f6dc764..f4dc3cc 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -431,14 +431,55 @@ impl<'ctx> LLVMGenerator<'ctx> { let rhs = self.generate_expression(rhs).into_int_value(); // TODO implement the rest of the binary expressions match token.token_type { - TokenType::EqualsEquals => self.builder.build_int_compare(inkwell::IntPredicate::EQ, lhs, rhs, "bool_value"), - TokenType::NotEquals => self.builder.build_int_compare(inkwell::IntPredicate::NE, lhs, rhs, "bool_value"), - TokenType::GreaterThan => self.builder.build_int_compare(inkwell::IntPredicate::SGT, lhs, rhs, "bool_value"), - TokenType::GreaterThanEquals => self.builder.build_int_compare(inkwell::IntPredicate::SGE, lhs, rhs, "bool_value"), - TokenType::LessThan => self.builder.build_int_compare(inkwell::IntPredicate::SLT, lhs, rhs, "bool_value"), - TokenType::LessThanEquals => self.builder.build_int_compare(inkwell::IntPredicate::SLE, lhs, rhs, "bool_value"), - _ => panic!() - }.unwrap().as_basic_value_enum() + TokenType::EqualsEquals => { + self.builder + .build_int_compare(inkwell::IntPredicate::EQ, lhs, rhs, "bool_value") + } + TokenType::NotEquals => { + self.builder + .build_int_compare(inkwell::IntPredicate::NE, lhs, rhs, "bool_value") + } + TokenType::GreaterThan => { + self.builder + .build_int_compare(inkwell::IntPredicate::SGT, lhs, rhs, "bool_value") + } + TokenType::GreaterThanEquals => { + self.builder + .build_int_compare(inkwell::IntPredicate::SGE, lhs, rhs, "bool_value") + } + TokenType::LessThan => { + self.builder + .build_int_compare(inkwell::IntPredicate::SLT, lhs, rhs, "bool_value") + } + TokenType::LessThanEquals => { + self.builder + .build_int_compare(inkwell::IntPredicate::SLE, lhs, rhs, "bool_value") + } + TokenType::Plus => self.builder.build_int_add(lhs, rhs, "temp_add"), + TokenType::Minus => self.builder.build_int_sub(lhs, rhs, "temp_sub"), + TokenType::Star => self.builder.build_int_mul(lhs, rhs, "temp_mul"), + TokenType::Slash => self.builder.build_int_signed_div(lhs, rhs, "temp_div"), + TokenType::And => self.builder.build_and(lhs, rhs, "temp_and"), + TokenType::Bar => self.builder.build_or(lhs, rhs, "temp_or"), + TokenType::AndAnd | TokenType::BarBar => { + let lhs = self + .builder + .build_int_cast(lhs, self.context.bool_type(), "bool_lhs") + .unwrap(); + let rhs = self + .builder + .build_int_cast(rhs, self.context.bool_type(), "bool_rhs") + .unwrap(); + if token.token_type == TokenType::AndAnd { + self.builder.build_and(lhs, rhs, "temp_logical_and") + } else { + self.builder.build_or(lhs, rhs, "temp_logical_or") + } + } + _ => panic!(), + } + .unwrap() + .as_basic_value_enum() } fn generate_variable_expression(&mut self, name: &Token) -> BasicValueEnum<'ctx> { From c82b80f2e16461869e5e3f187884de46293c4003 Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 29 Oct 2023 00:27:38 +0200 Subject: [PATCH 6/7] Support while loops --- src/code_generation/llvm/generator.rs | 23 +++++++++++++-------- src/tests/while.c | 29 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 src/tests/while.c diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index f4dc3cc..19d8fe5 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -57,7 +57,7 @@ impl<'ctx> LLVMGenerator<'ctx> { ExpressionNode(expression) => self.generate_expression(expression).as_any_value_enum(), Scope(..) => self.generate_scope(node).as_any_value_enum(), If(..) => self.generate_if_statement(node).as_any_value_enum(), - // While(..) => self.generate_while(node), + While(..) => self.generate_while(node).as_any_value_enum(), // DoWhile(..) => self.generate_do_while(node), ExpressionStatement(..) => self.generate_expression_statement(node).as_any_value_enum(), // For(..) => self.generate_for(node), @@ -565,16 +565,18 @@ impl<'ctx> LLVMGenerator<'ctx> { let cond_result = self.generate_expression(condition); let zero = self.context.i32_type().const_int(0, false); - let i32_value = self.builder.build_int_z_extend(cond_result.into_int_value(), self.context.i32_type(), "extended_condition").unwrap(); - let bool_value = self + let i32_value = self .builder - .build_int_compare( - inkwell::IntPredicate::NE, - i32_value, - zero, - "bool_value", + .build_int_z_extend( + cond_result.into_int_value(), + self.context.i32_type(), + "extended_condition", ) .unwrap(); + let bool_value = self + .builder + .build_int_compare(inkwell::IntPredicate::NE, i32_value, zero, "bool_value") + .unwrap(); self.builder .build_conditional_branch(bool_value, then_block, else_block) .unwrap(); @@ -719,6 +721,11 @@ mod tests { run_tests_from_file("./src/tests/if.c"); } + #[test] + fn test_while() { + run_tests_from_file("./src/tests/while.c"); + } + #[test] fn test_assignment() { run_tests_from_file("./src/tests/assignment.c"); diff --git a/src/tests/while.c b/src/tests/while.c new file mode 100644 index 0000000..c70c9fc --- /dev/null +++ b/src/tests/while.c @@ -0,0 +1,29 @@ +// CASE Basic while loop +// RETURNS 5 + +int main() { + int x = 0; + int y = 5; + while (y > 0) { + y = y - 1; + x = x + 1; + } + return x; +} + +// CASE 45th Fibonacci +// RETURNS 1836311903 + +int main() { + int x = 45; + int a = 0; + int b = 1; + int c; + while (x) { + c = a + b; + a = b; + b = c; + x = x - 1; + } + return c; +} \ No newline at end of file From 493fcd7ef4ad128e4864609fd9c269e753bf663a Mon Sep 17 00:00:00 2001 From: Osama Ahmad Date: Sun, 29 Oct 2023 00:38:37 +0200 Subject: [PATCH 7/7] LLVM: test exit codes are taken mod 256 --- src/code_generation/llvm/generator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code_generation/llvm/generator.rs b/src/code_generation/llvm/generator.rs index 19d8fe5..69799f4 100644 --- a/src/code_generation/llvm/generator.rs +++ b/src/code_generation/llvm/generator.rs @@ -698,7 +698,7 @@ mod tests { code_generation::llvm::generator::LLVMGenerator::new(&mut context).generate(&ast); let exit_code = interpret_llvm_ir(&generated_ir); assert_eq!( - test_case.expected, exit_code, + test_case.expected % 256, exit_code % 256, "Test case: {} -- Expected: {}, found: {}\nGenerated IR:\n{}", test_case.name, test_case.expected, exit_code, generated_ir );