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

Add unsigned integers #22

Merged
merged 9 commits into from
Aug 9, 2024
2 changes: 1 addition & 1 deletion examples/extern.fl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern fn putchar(i8 c) i8


pub fn main() {
i64 x = 4
u64 x = 4
if x > 3 {
i8 d = putchar(72)
}
Expand Down
4 changes: 2 additions & 2 deletions examples/return_in_while_loop.fl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub fn main() i64 {
i64 i = 0
pub fn main() u64 {
u64 i = 0
while i < 10 {
i += 1
ret i
Expand Down
4 changes: 2 additions & 2 deletions examples/simple_while_loop.fl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub fn main() i64 {
i64 i = 1
pub fn main() u64 {
u64 i = 1
while i < 10 {
i += 1
}
Expand Down
10 changes: 10 additions & 0 deletions examples/unsigned_ints.fl
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub fn main() i8 {
i8 a = 130
i8 b = 120
if a < b {
ret 1
} else {
ret -1
}
ret 0
}
57 changes: 41 additions & 16 deletions src/compilation/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::mem::MaybeUninit;
use std::path::Path;

use llvm_sys::core::*;
use llvm_sys::LLVMIntPredicate;
use llvm_sys::error::LLVMGetErrorMessage;
use llvm_sys::prelude::*;
use llvm_sys::target::{
Expand All @@ -21,12 +22,11 @@ use llvm_sys::target_machine::{
LLVMTargetMachineRef,
};
use llvm_sys::transforms::pass_builder::*;
use llvm_sys::LLVMIntPredicate::*;
use llvm_sys::LLVMLinkage::{LLVMExternalLinkage, LLVMInternalLinkage};

use crate::ast::*;
use crate::typed_ast::*;
use crate::types::FuncType;
use crate::types::{FuncType, IntType};
use crate::ScopeManager;
use crate::Type;

Expand Down Expand Up @@ -503,37 +503,62 @@ impl Compiler {
panic!("Binary expr: type(LHS) != type(RHS) should've been handled by Typer")
}

let int_type = match bin_expr.result_type {
Type::Int(int_type) => int_type,
_ => panic!("Unsupported lhs and rhs types for binary expr; can only handle integers"),
};

match bin_expr.operator {
Add => LLVMBuildAdd(self.builder, lhs, rhs, cstr!("add")),
Subtract => LLVMBuildSub(self.builder, lhs, rhs, cstr!("sub")),
Multiply => LLVMBuildMul(self.builder, lhs, rhs, cstr!("mul")),
// TODO: signed-ints: Signed vs unsigned division
Divide => LLVMBuildSDiv(self.builder, lhs, rhs, cstr!("sdiv")),
Divide => match int_type {
IntType { signed: true, .. } => LLVMBuildSDiv(self.builder, lhs, rhs, cstr!("sdiv")),
IntType { signed: false, .. } => LLVMBuildUDiv(self.builder, lhs, rhs, cstr!("udiv")),
},
}
}

/// Compiles a comparison expression.
///
/// Note that this function accept an `expected_type`, only to confirm that it is `i1` (boolean),
/// since comparison expressions.
unsafe fn compile_comparison_expr(&mut self, comparison: &TypedComparison) -> LLVMValueRef {
use ComparisonOperator::*;

let lhs = self.compile_expr(&comparison.left);
let rhs = self.compile_expr(&comparison.right);


if LLVMTypeOf(lhs) != LLVMTypeOf(rhs) {
panic!("Comparison: type(LHS) != type(RHS) should've been handled by Typer")
}

// TODO: Signed comparisons
match comparison.operator {
NotEqualTo => LLVMBuildICmp(self.builder, LLVMIntNE, lhs, rhs, cstr!("neq")),
EqualTo => LLVMBuildICmp(self.builder, LLVMIntEQ, lhs, rhs, cstr!("eq")),
LessThan => LLVMBuildICmp(self.builder, LLVMIntSLT, lhs, rhs, cstr!("slt")),
GreaterThan => LLVMBuildICmp(self.builder, LLVMIntSGT, lhs, rhs, cstr!("sgt")),
LessOrEqualTo => LLVMBuildICmp(self.builder, LLVMIntSLE, lhs, rhs, cstr!("sle")),
GreaterOrEqualTo => LLVMBuildICmp(self.builder, LLVMIntSGE, lhs, rhs, cstr!("sge")),
match comparison.operand_type {
Type::Int(int_type) => LLVMBuildICmp(self.builder, self.comparison_int_op(comparison.operator, int_type), lhs, rhs, cstr!("")),
_ => panic!("Unsupported lhs and rhs types for comparison; can only handle integers"),
}

}

unsafe fn comparison_int_op(&mut self, operator: ComparisonOperator, result_type: IntType) -> LLVMIntPredicate {
use LLVMIntPredicate::*;

match operator {
ComparisonOperator::NotEqualTo => LLVMIntNE,
ComparisonOperator::EqualTo => LLVMIntEQ,
ComparisonOperator::LessThan => match result_type {
IntType { signed: true, .. } => LLVMIntSLT,
IntType { signed: false, .. } => LLVMIntULT,
},
ComparisonOperator::GreaterThan => match result_type {
IntType { signed: true, .. } => LLVMIntSGT,
IntType { signed: false, .. } => LLVMIntUGT,
},
ComparisonOperator::LessOrEqualTo => match result_type {
IntType { signed: true, .. } => LLVMIntSLE,
IntType { signed: false, .. } => LLVMIntULE,
},
ComparisonOperator::GreaterOrEqualTo => match result_type {
IntType { signed: true, .. } => LLVMIntSGE,
IntType { signed: false, .. } => LLVMIntUGE,
},
}
}

Expand Down
12 changes: 8 additions & 4 deletions src/lexing/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,14 @@ impl<'a> Lexer<'a> {
fn read_word(&mut self) -> Token {
let s = self.take_chars_while(|&c| c.is_ascii_alphanumeric() || c == '_');

if s.starts_with('i') && s.len() > 1 && s.chars().skip(1).all(|c| c.is_ascii_digit()) {
if (s.starts_with('u') || s.starts_with('i')) && s.len() > 1 && s.chars().skip(1).all(|c| c.is_ascii_digit()) {
let num: String = s.chars().skip(1).collect();
let width: u32 = num.parse().unwrap();
return Token::Type(Type::Int(IntType { width }));
match s.chars().next().unwrap() {
'u' => return Token::Type(Type::Int(IntType { width, signed: false })),
'i' => return Token::Type(Type::Int(IntType { width, signed: true })),
_ => unreachable!(),
}
}

match s.as_str() {
Expand Down Expand Up @@ -245,12 +249,12 @@ mod tests {
fn variables() {
let source_code = "i64 this_is_a_LONG_VARIABLE_NAME = 5\ni64 shortInt = 5";
let expected_tokens = vec![
Token::Type(Type::Int(IntType { width: 64 })),
Token::Type(Type::Int(IntType { signed: true, width: 64 })),
Token::Identifier("this_is_a_LONG_VARIABLE_NAME".to_string()),
Token::AssignmentSymbol(Eq),
Token::IntLiteral("5".to_string()),
Token::Newline,
Token::Type(Type::Int(IntType { width: 64 })),
Token::Type(Type::Int(IntType { signed: true, width: 64 })),
Token::Identifier("shortInt".to_string()),
Token::AssignmentSymbol(Eq),
Token::IntLiteral("5".to_string()),
Expand Down
12 changes: 6 additions & 6 deletions src/parsing/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,14 @@ mod tests {
#[test]
fn var_declaration() {
let tokens = vec![
Token::Type(Type::Int(IntType { width: 64 })),
Token::Type(Type::Int(IntType { signed: true, width: 64 })),
Token::Identifier("x".to_string()),
Token::AssignmentSymbol(Eq),
Token::IntLiteral("5".to_string()),
];
let expected = Some(Statement::VarDeclaration(VarDeclaration {
var_name: "x".to_string(),
var_type: Type::Int(IntType { width: 64 }),
var_type: Type::Int(IntType { signed: true, width: 64 }),
var_value: Expr::IntLiteral(IntLiteral { negative: false, value: "5".to_string() }),
}));

Expand Down Expand Up @@ -799,10 +799,10 @@ mod tests {
Token::Fn,
Token::Identifier("test".to_string()),
Token::LParen,
Token::Type(Type::Int(IntType { width: 64 })),
Token::Type(Type::Int(IntType { signed: true, width: 64 })),
Token::Identifier("a".to_string()),
Token::RParen,
Token::Type(Type::Int(IntType { width: 64 })),
Token::Type(Type::Int(IntType { signed: true, width: 64 })),
Token::LSquirly,
Token::RSquirly,
];
Expand All @@ -812,10 +812,10 @@ mod tests {
func_visibility: FuncVisibility::Public,
name: "test".to_string(),
params: vec![FuncParam {
param_type: Type::Int(IntType { width: 64 }),
param_type: Type::Int(IntType { signed: true, width: 64 }),
param_name: "a".to_string(),
}],
return_type: Type::Int(IntType { width: 64 }),
return_type: Type::Int(IntType { signed: true, width: 64 }),
},
body: vec![],
})],
Expand Down
6 changes: 5 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ impl fmt::Display for Type {
/// An enum to store the built-in int type
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct IntType {
pub signed: bool,
pub width: u32,
}

impl fmt::Display for IntType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "i{}", self.width)
match self.signed {
true => write!(f, "i{}", self.width),
false => write!(f, "u{}", self.width),
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/typing/typed_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ pub struct TypedComparison {
pub left: Box<TypedExpr>,
pub operator: ComparisonOperator,
pub right: Box<TypedExpr>,
pub operand_type: Type,
}

/// A call expression (the name of the function to call and the arguments to pass).
Expand Down
47 changes: 27 additions & 20 deletions src/typing/typer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl Typer {
/// This method checks that an if statement has a *boolean* condition and a collection of body
/// statements that are well-typed.
fn type_if_statement(&mut self, if_statement: &If, function_return_type: &Type) -> TypedIf {
let bool_type = Type::Int(IntType { width: 1 });
let bool_type = Type::Int(IntType { width: 1, signed: false });
let condition = self.type_expr(&if_statement.condition, Some(&bool_type));
let then_body = self.type_body(&if_statement.then_body, function_return_type);
let else_body = if_statement.else_body.as_ref().map(|body| self.type_body(body, function_return_type));
Expand All @@ -184,7 +184,7 @@ impl Typer {
while_loop: &WhileLoop,
function_return_type: &Type,
) -> TypedWhileLoop {
let bool_type = Type::Int(IntType { width: 1 });
let bool_type = Type::Int(IntType { width: 1, signed: false });
let condition = self.type_expr(&while_loop.condition, Some(&bool_type));
let body = self.type_body(&while_loop.body, function_return_type);
TypedWhileLoop { condition, body }
Expand Down Expand Up @@ -271,13 +271,19 @@ impl Typer {
Some(Type::Int(int_type)) => *int_type,
Some(t) => {
panic!(
"expected integer type for literal '{}', but the desired type is '{}'",
"Expected integer type for literal '{}', but the desired type is '{}'",
int_literal, t
)
}
None => IntType { width: 64 },
None => IntType { signed: false, width: 64 },
};

if int_literal.negative && !int_type.signed {
panic!("Expected unsigned integer type '{}', but got negative int_literal '{}'", int_type, int_literal);
}

// TODO: Ensure that the int_literal.value fits within the desired width

TypedIntLiteral {
negative: int_literal.negative,
int_value: int_literal.value.clone(),
Expand Down Expand Up @@ -314,9 +320,9 @@ impl Typer {
comparison: &Comparison,
desired_type: Option<&Type>,
) -> TypedComparison {
if desired_type.is_some() && desired_type != Some(&Type::Int(IntType { width: 1 })) {
if desired_type.is_some() && desired_type != Some(&Type::Int(IntType { width: 1, signed: false })) {
panic!(
"Comparison returns an i1 but expected '{}'",
"Comparison expressions return an i1 but expected '{}'",
desired_type.unwrap()
);
}
Expand All @@ -340,6 +346,7 @@ impl Typer {
left: Box::new(left),
operator,
right: Box::new(right),
operand_type: left_type, // since both types must be equal
}
}

Expand Down Expand Up @@ -398,7 +405,7 @@ impl Typer {
TypedExpr::Identifier(id) => id.id_type.clone(),
TypedExpr::IntLiteral(int) => Type::Int(int.int_type),
TypedExpr::Binary(binary) => binary.result_type.clone(),
TypedExpr::Comparison(_) => Type::Int(IntType { width: 1 }),
TypedExpr::Comparison(_) => Type::Int(IntType { width: 1, signed: false }),
TypedExpr::Call(call) => Type::Func(call.function_type.clone()),
}
}
Expand Down Expand Up @@ -434,23 +441,23 @@ mod tests {
func_visibility: FuncVisibility::Public,
name: "main".to_string(),
params: vec![],
return_type: Type::Int(IntType { width: 32 }),
return_type: Type::Int(IntType { width: 32, signed: true }),
},
body: vec![
Statement::VarDeclaration(VarDeclaration {
var_name: "a".to_string(),
var_value: Expr::IntLiteral(IntLiteral { negative: false, value: "3".to_string() }),
var_type: Type::Int(IntType { width: 64 }),
var_type: Type::Int(IntType { signed: true, width: 64 }),
}),
Statement::VarDeclaration(VarDeclaration {
var_name: "b".to_string(),
var_value: Expr::Identifier("a".to_string()),
var_type: Type::Int(IntType { width: 64 }),
var_type: Type::Int(IntType { signed: true, width: 64 }),
}),
Statement::VarDeclaration(VarDeclaration {
var_name: "c".to_string(),
var_value: Expr::Identifier("b".to_string()), // this should panic, since b (i64) can't be in c (i32)
var_type: Type::Int(IntType { width: 32 }),
var_type: Type::Int(IntType { width: 32, signed: true }),
}),
],
})],
Expand All @@ -474,18 +481,18 @@ mod tests {
func_visibility: FuncVisibility::Public,
name: "main".to_string(),
params: vec![],
return_type: Type::Int(IntType { width: 32 }),
return_type: Type::Int(IntType { width: 32, signed: true }),
},
body: vec![
Statement::VarDeclaration(VarDeclaration {
var_name: "a".to_string(),
var_value: Expr::IntLiteral(IntLiteral { negative: false, value: "3".to_string() }),
var_type: Type::Int(IntType { width: 32 }),
var_type: Type::Int(IntType { width: 32, signed: true }),
}),
Statement::VarDeclaration(VarDeclaration {
var_name: "b".to_string(),
var_value: Expr::Identifier("a".to_string()),
var_type: Type::Int(IntType { width: 32 }),
var_type: Type::Int(IntType { width: 32, signed: true }),
}),
Statement::Return(Some(Expr::Identifier("b".to_string()))),
],
Expand All @@ -498,29 +505,29 @@ mod tests {
func_visibility: FuncVisibility::Public,
name: "main".to_string(),
params: vec![],
return_type: Type::Int(IntType { width: 32 }),
return_type: Type::Int(IntType { width: 32, signed: true }),
},
body: vec![
TypedStatement::VarDeclaration(TypedVarDeclaration {
var_name: "a".to_string(),
var_value: TypedExpr::IntLiteral(TypedIntLiteral {
negative: false,
int_value: "3".to_string(),
int_type: IntType { width: 32 },
int_type: IntType { width: 32, signed: true },
}),
var_type: Type::Int(IntType { width: 32 }),
var_type: Type::Int(IntType { width: 32, signed: true }),
}),
TypedStatement::VarDeclaration(TypedVarDeclaration {
var_name: "b".to_string(),
var_type: Type::Int(IntType { width: 32 }),
var_type: Type::Int(IntType { width: 32, signed: true }),
var_value: TypedExpr::Identifier(TypedIdentifier {
name: "a".to_string(),
id_type: Type::Int(IntType { width: 32 }),
id_type: Type::Int(IntType { width: 32, signed: true }),
}),
}),
TypedStatement::Return(Some(TypedExpr::Identifier(TypedIdentifier {
name: "b".to_string(),
id_type: Type::Int(IntType { width: 32 }),
id_type: Type::Int(IntType { width: 32, signed: true }),
}))),
],
})],
Expand Down