diff --git a/.gitignore b/.gitignore index 9f97022..9340bbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -target/ \ No newline at end of file +target/ +*.ll + +out +out.* \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index f3bf43f..9aab5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,8 @@ version = "0.1.0" dependencies = [ "commons", "inkwell", + "lexer", + "parser", ] [[package]] @@ -171,6 +173,8 @@ dependencies = [ name = "test-main" version = "0.1.0" dependencies = [ + "inkwell", + "ir", "lexer", "parser", ] diff --git a/ast.txt b/ast.txt new file mode 100644 index 0000000..4da05e4 --- /dev/null +++ b/ast.txt @@ -0,0 +1,198 @@ +Calling function call parsing on kwd abc +Tok: Dot +Tok: Var +Calling function call parsing on kwd abs +Tok: ComparingOperator( + HigherEqual, +) +Calling function call parsing on kwd def +Tok: If +Ok( + ParserCtx { + map: { + "main": FunctionDeclaration { + func_name: WithHash { + val: "main", + hash: 14467718814232352107, + }, + args: [ + FunctionDeclarationArgument { + name: WithHash { + val: "myTestArg", + hash: 14273091031817137443, + }, + argument_type: 11583643502875509928, + }, + FunctionDeclarationArgument { + name: WithHash { + val: "age", + hash: 11942856677619760021, + }, + argument_type: 11583643502875509928, + }, + ], + body: [ + VarDeclaration { + var_name: WithHash { + val: "test", + hash: 14402189752926126668, + }, + var_type: 11583643502875509928, + value: Some( + StructLRVariable { + l: StructLRFunction { + l: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + r: FunctionCall { + func: WithHash { + val: "abc", + hash: 17224545590736200989, + }, + args: [], + }, + }, + r: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + }, + ), + }, + VarDeclaration { + var_name: WithHash { + val: "test2", + hash: 12720938724704517959, + }, + var_type: 11583643502875509928, + value: Some( + OperatorBasedConditionMember { + lval: StructLRFunction { + l: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + r: FunctionCall { + func: WithHash { + val: "abs", + hash: 10550511239753159873, + }, + args: [], + }, + }, + rval: StructLRFunction { + l: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + r: FunctionCall { + func: WithHash { + val: "def", + hash: 9341360902760800985, + }, + args: [], + }, + }, + operator: HigherEqual, + }, + ), + }, + IfStatement { + cond: OperatorBasedConditionMember { + lval: VariableReference( + WithHash { + val: "test2", + hash: 12720938724704517959, + }, + ), + rval: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + operator: HigherEqual, + }, + body: [ + VarDeclaration { + var_name: WithHash { + val: "age", + hash: 11942856677619760021, + }, + var_type: 11583643502875509928, + value: Some( + IntegerLit( + 0, + ), + ), + }, + ], + else_statement: Some( + IfElseStatement { + cond: Some( + OperatorBasedConditionMember { + lval: VariableReference( + WithHash { + val: "test2", + hash: 12720938724704517959, + }, + ), + rval: VariableReference( + WithHash { + val: "test", + hash: 14402189752926126668, + }, + ), + operator: Equal, + }, + ), + body: [ + VarDeclaration { + var_name: WithHash { + val: "age", + hash: 11942856677619760021, + }, + var_type: 11583643502875509928, + value: Some( + IntegerLit( + 63, + ), + ), + }, + ], + else_statement: Some( + ElseStatement { + body: [ + VarDeclaration { + var_name: WithHash { + val: "age", + hash: 11942856677619760021, + }, + var_type: 11583643502875509928, + value: Some( + IntegerLit( + 68, + ), + ), + }, + ], + }, + ), + }, + ), + }, + ], + returnType: None, + }, + }, + }, +) \ No newline at end of file diff --git a/commons/src/err/mod.rs b/commons/src/err/mod.rs index 4777486..1bb477d 100644 --- a/commons/src/err/mod.rs +++ b/commons/src/err/mod.rs @@ -9,6 +9,7 @@ use colored::Colorize; use crate::Position; pub type PositionedResult = Result; +pub type PositionlessResult = Result; /// An error that has a position #[derive(Debug)] @@ -18,6 +19,21 @@ pub struct PositionedError { pub reason: String } +#[derive(Debug)] +pub struct PositionlessError { + pub reason: String +} + +impl PositionlessError { + pub fn new(reason: &str) -> Self { + let err = PositionlessError { reason: String::from(reason) }; + + println!("{}", err); + + return err; + } +} + impl PositionedError { pub fn new(start: Position, end: Position, reason: String) -> Self { @@ -48,6 +64,16 @@ impl fmt::Display for PositionedError { writeln!(f, "")?; writeln!(f, "{}", self.reason.bright_red())?; + Ok(()) + } +} + +impl fmt::Display for PositionlessError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{} at ??:??", "ERR".bright_red().bold())?; + + writeln!(f, "{}", self.reason.bright_red())?; + Ok(()) } } \ No newline at end of file diff --git a/commons/src/utils/map.rs b/commons/src/utils/map.rs index 24501b1..3a3881e 100644 --- a/commons/src/utils/map.rs +++ b/commons/src/utils/map.rs @@ -114,6 +114,21 @@ impl HashedMap { return None; } + pub fn entries(&self) -> Vec<&(u64, V)> { + let mut vec = Vec::new(); + + for i in 0..self.capacity { + if self.meta[i] == BUCKET_EMPTY || self.meta[i] == BUCKET_TOMBSTONE { + continue; + } + + unsafe { vec.push(self.buckets[i].assume_init_ref()); } + } + + return vec; + } + + pub fn erase(&mut self, key: u64) { let index = self.index_from_hash(key); let fingerprint = self.fingerprint_from_hash(key); diff --git a/commons/src/utils/mod.rs b/commons/src/utils/mod.rs index 36c02b6..7b7c82b 100644 --- a/commons/src/utils/mod.rs +++ b/commons/src/utils/mod.rs @@ -1 +1,2 @@ -pub mod map; \ No newline at end of file +pub mod map; +pub mod num; \ No newline at end of file diff --git a/commons/src/utils/num.rs b/commons/src/utils/num.rs new file mode 100644 index 0000000..f819531 --- /dev/null +++ b/commons/src/utils/num.rs @@ -0,0 +1,19 @@ +pub fn get_signed_highbound(bits: usize) -> i128 { + return 2_i128.pow(bits as u32 - 1) - 1; +} + +pub fn get_signed_lowbound(bits: usize) -> i128 { + return 0 - 2_i128.pow(bits as u32 - 1) - 1; +} + +pub fn get_unsigned_highbound(bits: usize) -> i128 { + return 2_i128.pow(bits as u32) - 1; +} + +pub fn can_num_fit_inbits_signed(bits: usize, num: i128) -> bool { + return num >= get_signed_lowbound(bits) && num <= get_signed_highbound(bits); +} + +pub fn can_num_fit_inbits_unsigned(bits: usize, num: i128) -> bool { + return num >= 0 && num <= get_unsigned_highbound(bits); +} diff --git a/ir/Cargo.toml b/ir/Cargo.toml index db53bab..7b48c9a 100644 --- a/ir/Cargo.toml +++ b/ir/Cargo.toml @@ -5,4 +5,6 @@ edition = "2024" [dependencies] inkwell = { version = "0.8.0", features = ["llvm21-1"]} -commons = { path = "../commons" } \ No newline at end of file +commons = { path = "../commons" } +lexer = { path = "../lexer" } +parser = { path = "../parser" } \ No newline at end of file diff --git a/ir/src/bools.rs b/ir/src/bools.rs new file mode 100644 index 0000000..d21ec42 --- /dev/null +++ b/ir/src/bools.rs @@ -0,0 +1,80 @@ +//! Everything related to boolean handling and generation + +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::IntPredicate; +use lexer::toks::comp::ComparingOperator; + +use crate::{ctx::IRContext, refs::IRValueRef, types::BOOL_TYPE_HASH, values::IRValue}; + +pub fn make_bool_xor<'a>(ctx: &'a IRContext<'a>, b: IRValueRef<'a>) -> PositionlessResult> { + let val = b.obtain(ctx)?; + let inkwell = match val.obtain_as_bool() { + Some(v) => v, + None => return Err(PositionlessError::new("Cannot obtain boolean from the provided boolean value reference!")) + }; + + let bool_t = match ctx.type_storage.get(BOOL_TYPE_HASH) { + Some(v) => v, + None => return Err(PositionlessError::new("Cannot find boolean type in type storage!")) + }; + + let one = bool_t.get_inkwell_inttype()?.const_int(1, false); + + let xor_v = match ctx.builder.build_xor(inkwell, one, "xor_") { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_xor failed!")) + }; + + return Ok(IRValue::new(xor_v.into(), bool_t)); +} + +pub fn make_bool_cmp_int<'a>(ctx: &'a IRContext<'a>, a: IRValueRef<'a>, b: IRValueRef<'a>, comp: ComparingOperator) -> PositionlessResult> { + let a_val = a.obtain(ctx)?; + let b_val = b.obtain(ctx)?; + + let t = a_val.t; + + let a_int = match a_val.obtain_as_int(t) { + Some(v) => v, + None => return Err(PositionlessError::new("Value a wasn't an int when trying to use comp!")) + }; + + let b_int = match b_val.obtain_as_int(t) { + Some(v) => v, + None => return Err(PositionlessError::new("Value b wasn't an int when trying to use comp!")) + }; + + let predicate: IntPredicate; + + if t.is_signed() { + predicate = match comp { + ComparingOperator::Equal => IntPredicate::EQ, + ComparingOperator::NotEqual => IntPredicate::NE, + ComparingOperator::Higher => IntPredicate::SGT, + ComparingOperator::HigherEqual => IntPredicate::SGE, + ComparingOperator::Lower => IntPredicate::SLT, + ComparingOperator::LowerEqual => IntPredicate::SLE + }; + } else { + predicate = match comp { + ComparingOperator::Equal => IntPredicate::EQ, + ComparingOperator::NotEqual => IntPredicate::NE, + ComparingOperator::Higher => IntPredicate::ULT, + ComparingOperator::HigherEqual => IntPredicate::UGT, + ComparingOperator::Lower => IntPredicate::ULE, + ComparingOperator::LowerEqual => IntPredicate::UGE + } + } + + let cmp = match ctx.builder.build_int_compare(predicate, a_int, b_int, "_cmp") { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_int_compare failed!")) + }; + + let bool_t = match ctx.type_storage.get(BOOL_TYPE_HASH) { + Some(v) => v, + None => return Err(PositionlessError::new("boolean type wasn't found!")) + }; + + return Ok(IRValue::new(cmp.into(), bool_t)); +} \ No newline at end of file diff --git a/ir/src/conv/func.rs b/ir/src/conv/func.rs new file mode 100644 index 0000000..0c65624 --- /dev/null +++ b/ir/src/conv/func.rs @@ -0,0 +1,62 @@ +use commons::err::{PositionlessError, PositionlessResult}; +use parser::ast::tree::ASTTreeNode; + +use crate::{conv::val::parse_ir_value, ctx::IRContext, irstruct::{funcs::IRFunction, ptr::IRPointer}, refs::IRValueRef, types::typing::IRType}; + +pub fn parse_ir_function_decl<'a>(ctx: &'a IRContext<'a>, node: Box) -> PositionlessResult> { + if let ASTTreeNode::FunctionDeclaration { func_name, args, body, returnType } = *node { + let return_type = match returnType { + Some(h) => ctx.type_storage.get(h), + None => None + }; + + let mut arguments: Vec<&'a IRType<'a>> = vec![]; + + for k in args { + let t = match ctx.type_storage.get(k.argument_type) { + Some(v) => v, + None => return Err(PositionlessError::new(&format!("Cannot get type with hash {} for argument {}!", k.argument_type, k.name.val))) + }; + + arguments.push(t); + } + + let func = IRFunction::create(ctx, func_name.val, &ctx.module, return_type, arguments)?; + + return Ok(func); + } + + return Err(PositionlessError::new("Given node in parse_ir_function_decl wasn't a function decl!")); +} + +pub fn parse_ir_function_body_member<'a>(ctx: &'a IRContext<'a>, func: &'a mut IRFunction<'a>, node: Box) -> PositionlessResult { + + let mut lctx = func.lctx.borrow_mut(); + + match *node { + ASTTreeNode::VarDeclaration { var_name, var_type, value } => { + let var_t = match ctx.type_storage.get(var_type) { + Some(v) => v, + None => return Err(PositionlessError::new(&format!("Cannot find variable type {} in type storage!", var_name.val))) + }; + + { + let initial = if let Some(v) = value { + Some(parse_ir_value(&mut lctx, ctx, v, None)?) + } else { + None + }; + } + + let ptr = IRPointer::create(ctx, var_name.val.clone(), var_t, None)?; + + let ptr = IRPointer::create(ctx, var_name.val, var_t, None)?; + + lctx.add_variable(var_name.hash, ptr); + + return Ok(true); + }, + + _ => return Err(PositionlessError::new("Cannot parse said ASTNode as a function body member!")) + }; +} \ No newline at end of file diff --git a/ir/src/conv/mod.rs b/ir/src/conv/mod.rs new file mode 100644 index 0000000..ac739a8 --- /dev/null +++ b/ir/src/conv/mod.rs @@ -0,0 +1,4 @@ +//! Critical AST -> IR conversion code module + +pub mod val; +pub mod func; \ No newline at end of file diff --git a/ir/src/conv/val.rs b/ir/src/conv/val.rs new file mode 100644 index 0000000..8a4a02e --- /dev/null +++ b/ir/src/conv/val.rs @@ -0,0 +1,156 @@ +//! AST value -> IR value conversion + +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::values::BasicValue; +use parser::ast::tree::ASTTreeNode; + +use crate::{bools::{make_bool_cmp_int, make_bool_xor}, ctx::{IRContext, IRLocalContext}, irstruct::{funcs::IRFunction, ptr::IRPointer, staticvars::IRStaticVariable}, math::make_math_operation, refs::IRValueRef, types::{POINTER_TYPE_HASH, SIGNED64_TYPE_HASH}, values::IRValue}; + +pub fn get_variable_ref<'a>(lctx: &'a IRLocalContext<'a>, ctx: &'a IRContext<'a>, hash: u64) -> PositionlessResult> { + match ctx.get_variable(hash) { + Ok(v) => return Ok(IRValueRef::from_static(IRStaticVariable::clone(v))), + Err(_) => {} + }; + + match lctx.get_variable(hash) { + Ok(v) => return Ok(IRValueRef::from_pointer(IRPointer::clone(v))), + Err(_) => return Err(PositionlessError::new(&format!("Cannot find variable with hash {} in the current context", hash))) + }; +} + +pub fn parse_ir_value<'a>(func: Option<&'a IRFunction<'a>>, ctx: &'a IRContext<'a>, node: Box, left: Option>) -> PositionlessResult> { + match node.as_ref() { + ASTTreeNode::IntegerLit(v) => { + let t = ctx.type_storage.get(SIGNED64_TYPE_HASH); + + if !t.is_some() { + return Err(PositionlessError::new("Invalid type storage! si64 not found!")); + } + + return Ok(IRValueRef::from_val(IRValue::from_signed(t.unwrap(), *v as i128)?)); + }, + + ASTTreeNode::StringLit(v) => { + let t = ctx.type_storage.get(POINTER_TYPE_HASH); + + if !t.is_some() { + return Err(PositionlessError::new("Invalid type storage! pointer not found!")); + } + + let global = IRStaticVariable::from_str(&ctx.builder, v, String::from("__string_literal"), t.unwrap())?; + + return Ok(IRValueRef::from_static(global)); + }, + + ASTTreeNode::VariableReference(e) => { + if left.as_ref().is_some() { + let struct_t = left.as_ref().unwrap().t.get_structured_type_descriptor()?; + + let ptr = struct_t.get_pointer_for_field_noref(ctx, left.unwrap(), e.hash)?; + + return Ok(IRValueRef::from_pointer(ptr)); + } + + let var = get_variable_ref(&func.unwrap().lctx.borrow(), ctx, e.hash)?; + + return Ok(var); + }, + + ASTTreeNode::FunctionCall { func, args } => { + let mut arguments = vec![]; + + if left.as_ref().is_some() { + arguments.push(IRValueRef::from_pointer(left.as_ref().unwrap().clone())); + } + + for arg in &args[0..args.len()] { + arguments.push(parse_ir_value(lctx, ctx, arg.clone(), None)?); + } + + let res: Option>; + + if left.is_some() { + let descriptor = left.as_ref().unwrap().t.get_structured_type_descriptor()?; + + let f = descriptor.get_function(func.hash)?; + + res = f.call(ctx, arguments, true)?; + } else { + let f = ctx.get_funtion(func.hash)?; + + res = f.call(ctx, arguments, true)?; + } + + if res.is_none() { + return Err(PositionlessError::new(&format!("Cannot use the result of function {} as a value as it is void!", func.val))); + } + + return Ok(IRValueRef::from_pointer(res.unwrap())); + }, + + ASTTreeNode::MathResult { lval, rval, operator, assigns } => { + let left = parse_ir_value(lctx, ctx, lval.clone(), None)?; + let right = parse_ir_value(lctx, ctx, rval.clone(), None)?; + + let t = left.get_type(); + + let l_val = match left.obtain(ctx)?.obtain_as_int(t) { + Some(v) => v, + None => return Err(PositionlessError::new("lval on math operation wasn't a number!")), + }; + + let r_val = match right.obtain(ctx)?.obtain_as_int(t) { + Some(v) => v, + None => return Err(PositionlessError::new("lval on math operation wasn't a number!")), + }; + + let out = make_math_operation(&ctx.builder, l_val, r_val, String::from("_math"), operator.clone())?; + + if *assigns { + if left.as_pointer().is_err() { + return Err(PositionlessError::new("Assignments were enabled on math operation while left value wasn't a variable!")); + } + + let ptr = left.as_pointer()?; + ptr.store(&ctx.builder, out.as_basic_value_enum()); + } + + return Ok(IRValueRef::from_val(IRValue::new(out.into(), t))); + }, + + ASTTreeNode::OperatorBasedConditionMember { lval, rval, operator } => { + let l_val = parse_ir_value(lctx, ctx, lval.clone(), None)?; + let r_val = parse_ir_value(lctx, ctx, rval.clone(), None)?; + + let cmp = make_bool_cmp_int(ctx, l_val, r_val, operator.clone())?; + + return Ok(IRValueRef::from_val(cmp)); + }, + + ASTTreeNode::BooleanBasedConditionMember { val, negate } => { + let v = parse_ir_value(lctx, ctx, val.clone(), None)?; + + if *negate { + return Ok(IRValueRef::from_val(make_bool_xor(ctx, v)?)) + } + + return Ok(v); + } + + ASTTreeNode::StructLRFunction { l, r } => { + let l_val = parse_ir_value(lctx, ctx, l.clone(), None)?; + let l_ptr = l_val.as_pointer()?; + + return parse_ir_value(lctx, ctx, r.clone(), Some(l_ptr)); + }, + + ASTTreeNode::StructLRVariable { l, r } => { + let l_val = parse_ir_value(lctx, ctx, l.clone(), None)?; + let l_ptr = l_val.as_pointer()?; + + return parse_ir_value(lctx, ctx, r.clone(), Some(l_ptr)); + } + + _ => return Err(PositionlessError::new("The given node cannot be parsed as a value!")) + } +} \ No newline at end of file diff --git a/ir/src/ctx.rs b/ir/src/ctx.rs new file mode 100644 index 0000000..55cf580 --- /dev/null +++ b/ir/src/ctx.rs @@ -0,0 +1,134 @@ +//! IR context related code + +use commons::{err::{PositionlessError, PositionlessResult}, utils::map::HashedMap}; +use inkwell::{AddressSpace, builder::Builder, context::Context, module::Module, types::{PointerType, VoidType}}; + +use crate::{irstruct::{funcs::IRFunction, ptr::IRPointer, staticvars::IRStaticVariable}, types::storage::IRTypeStorage}; + +/// The global IR context. +/// Basically holds anything related to the current IR compilation (eg: functions, types, global vars) +pub struct IRContext<'a> { + pub inkwell_ctx: &'a Context, + pub builder: Builder<'a>, + pub ptr_type: PointerType<'a>, + pub void_type: VoidType<'a>, + + pub module: Module<'a>, + + pub type_storage: IRTypeStorage<'a>, + + pub functions: HashedMap>, + pub static_vars: HashedMap> +} + +impl<'a> IRContext<'a> { + pub fn new(builder: Builder<'a>, ctx: &'a Context) -> Self { + return IRContext { inkwell_ctx: ctx, builder, ptr_type: ctx.ptr_type(AddressSpace::from(0)), functions: HashedMap::new(0), static_vars: HashedMap::new(0), type_storage: IRTypeStorage::new(&ctx), void_type: ctx.void_type(), module: ctx.create_module("quickfall_module") } + } + + pub fn add_variable(&'a mut self, hash: u64, var: IRStaticVariable<'a>) -> PositionlessResult { + if self.is_key_taken(hash) { + return Err(PositionlessError::new("There already is an element named like this!")); + } + + self.static_vars.put(hash, var); + return Ok(true); + } + + pub fn get_variable(&'a self, hash: u64) -> PositionlessResult<&'a IRStaticVariable<'a>> { + return match self.static_vars.get(hash) { + Some(v) => Ok(v), + None => return Err(PositionlessError::new("Invalid variable name")) + }; + } + + pub fn is_key_taken(&self, hash: u64) -> bool { + return self.functions.get(hash).is_some() || self.static_vars.get(hash).is_some() || self.type_storage.get(hash).is_some(); + } + + pub fn get_funtion(&'a self, hash: u64) -> PositionlessResult<&'a IRFunction<'a>> { + return match self.functions.get(hash) { + Some(v) => Ok(v), + None => Err(PositionlessError::new("Invalid function name!")) + } + } + + pub fn add_function(&'a mut self, hash: u64, func: IRFunction<'a>) -> PositionlessResult { + if self.is_key_taken(hash) { + return Err(PositionlessError::new("There already is an element named like this!")); + } + + self.functions.put(hash, func); + return Ok(true); + } + +} + +pub struct LocalIRVariable<'a> { + pub ptr: IRPointer<'a>, + pub depth: usize // Depth is depth in body. +} + +/// The local IR context. +/// Holds anything held and created in the given body (eg: vars). +pub struct IRLocalContext<'a> { + pub vars: HashedMap>, + pub current_depth: usize, // Starts at 0 where 0 is function body +} + +impl<'a> IRLocalContext<'a> { + pub fn new() -> Self { + return IRLocalContext { vars: HashedMap::new(0), current_depth: 0 } + } + + /// Attempts to add a variable in the current local context. Will return an error if the operation is impossible + pub fn add_variable(&mut self, hash: u64, var: IRPointer<'a>) -> PositionlessResult { + if self.vars.get(hash).is_some() { + return Err(PositionlessError::new(&format!("Variable named {} is already registered in the current context.", hash))); + } + + self.vars.put(hash, LocalIRVariable { ptr: var, depth: self.current_depth }); + return Ok(true); + } + + pub fn get_variable(&'a self, hash: u64) -> PositionlessResult<&'a IRPointer<'a>> { + return match self.vars.get(hash) { + Some(v) => Ok(&v.ptr), + None => return Err(PositionlessError::new("Invalid variable name")) + }; + } + + pub fn increment_body_depth(&mut self) { + self.current_depth += 1; + } + + /// Ends the current nested body. Is responsible for removing non-valid variable indices + /// Example: + /// ``` + /// func test() { + /// // body of depth 0 starts + /// if(true) { + /// // body of depth 1 starts + /// // body of depth 1 ends + /// } + /// + /// // body of depth 0 ends + /// } + /// ``` + pub fn end_nested_body_depth(&mut self) { + self.current_depth -= 1; + + let mut hashToRemove: Vec = vec![]; + + for entry in self.vars.entries() { + if entry.1.depth > self.current_depth { + hashToRemove.push(entry.0); + } + } + + for hash in hashToRemove { + self.vars.erase(hash); + } + } + +} \ No newline at end of file diff --git a/ir/src/irstruct/funcs.rs b/ir/src/irstruct/funcs.rs new file mode 100644 index 0000000..30f4e6a --- /dev/null +++ b/ir/src/irstruct/funcs.rs @@ -0,0 +1,125 @@ +use std::cell::RefCell; + +use commons::err::{PositionedError, PositionlessError, PositionlessResult}; +use inkwell::{basic_block::BasicBlock, builder::Builder, context::Context, module::Module, types::BasicType, values::{BasicValueEnum, FunctionValue, IntValue}}; + +use crate::{ctx::{IRContext, IRLocalContext}, irstruct::ptr::IRPointer, refs::IRValueRef, types::typing::IRType, values::IRValue}; + +pub struct IRFunction<'a> { + pub inkwell_func: FunctionValue<'a>, + ret_type: Option<&'a IRType<'a>>, + args: Vec<&'a IRType<'a>>, + name: String, + + pub lctx: RefCell>, + + entry: Option> +} + +impl<'a> IRFunction<'a> { + pub fn new(ctx: &'a IRContext, name: String, func: FunctionValue<'a>, ret_type: Option<&'a IRType<'a>>, args: Vec<&'a IRType<'a>>) -> Self { + + let block = ctx.inkwell_ctx.append_basic_block(func, "entry"); + + return IRFunction { inkwell_func: func, ret_type, args, name, entry: Some(block), lctx: IRLocalContext::new().into() } + } + + pub fn new_shadow(name: String, func: FunctionValue<'a>, ret_type: Option<&'a IRType<'a>>, args: Vec<&'a IRType<'a>>) -> Self { + return IRFunction { inkwell_func: func, ret_type, args, name, entry: None, lctx: IRLocalContext::new().into() } + } + + pub fn create_shadow(ctx: &'a IRContext, name: String, module: &Module<'a>, ret_type: Option<&'a IRType<'a>>, args: Vec<&'a IRType<'a>>) -> PositionlessResult { + let mut kargs = vec![]; + + for k in &args { + kargs.push(k.get_inkwell_base_metadatatype()?); + } + + let t = match ret_type { + Some(ret) => ret.get_inkwell_basetype()?.fn_type(&kargs, false), + None => ctx.void_type.fn_type(&kargs, false) + }; + + let func = module.add_function(&name, t, None); + + return Ok(IRFunction::new_shadow(name, func, ret_type, args)); + } + + pub fn create(ctx: &'a IRContext, name: String, module: &Module<'a>, ret_type: Option<&'a IRType<'a>>, args: Vec<&'a IRType<'a>>) -> PositionlessResult { + let mut kargs = vec![]; + + for k in &args { + kargs.push(k.get_inkwell_base_metadatatype()?); + } + + let t = match ret_type { + Some(ret) => ret.get_inkwell_basetype()?.fn_type(&kargs, false), + None => ctx.void_type.fn_type(&kargs, false) + }; + + let func = module.add_function(&name, t, None); + + return Ok(IRFunction::new(ctx, name, func, ret_type, args)); + } + + pub fn call(&'a self, ctx: &'a IRContext<'a>, args: Vec>, grab_return: bool) -> PositionlessResult>> { + let mut inkwell_args = vec![]; + + for arg in args { + inkwell_args.push(arg.obtain(ctx)?.obtain().into()); + } + + let call = match ctx.builder.build_call(self.inkwell_func, &inkwell_args, &self.name) { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_call failed!")) + }; + + if !grab_return { + return Ok(None); + } + + let return_type = match self.ret_type { + Some(ret) => ret, + None => return Ok(None) + }; + + let val = match call.try_as_basic_value().basic() { + Some(v) => v, + None => return Ok(None) + }; + + let val = IRValue::new(val, return_type); + + let pointer = IRPointer::create(ctx, format!("function_ret_{}", self.name), return_type, Some(IRValueRef::from_val(val)))?; + + return Ok(Some(pointer)); + } + + /// Prepares the addition of the function body. + pub fn prepare_body_filling(&self, builder: &Builder<'a>) { + if self.entry.is_none() { + return; + } + + builder.position_at_end(self.entry.unwrap()); + } + + pub fn get_nth_arg(&self, ind: u32) -> PositionlessResult> { + let res = match self.inkwell_func.get_nth_param(ind) { + Some(v) => v, + None => return Err(PositionlessError::new("Couldn't get nth param using get_nth_param")) + }; + + return Ok(res); + } + + pub fn get_nth_arg_int(&self, ind: u32) -> PositionlessResult> { + if !self.args[ind as usize].is_numeric_type() { + return Err(PositionlessError::new("Tried getting nth argument but given argument's type isn't numeric!")); + } + + return Ok(self.get_nth_arg(ind)?.into_int_value()); + + } + +} \ No newline at end of file diff --git a/ir/src/irstruct/mod.rs b/ir/src/irstruct/mod.rs new file mode 100644 index 0000000..5ad68ad --- /dev/null +++ b/ir/src/irstruct/mod.rs @@ -0,0 +1,6 @@ +//! Common definitions used internally to structure the IR. + +pub mod ptr; +pub mod funcs; +pub mod staticvars; +pub mod structs; \ No newline at end of file diff --git a/ir/src/irstruct/ptr.rs b/ir/src/irstruct/ptr.rs new file mode 100644 index 0000000..b51e616 --- /dev/null +++ b/ir/src/irstruct/ptr.rs @@ -0,0 +1,63 @@ +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::{builder::Builder, context::Context, types::BasicTypeEnum, values::{BasicValue, BasicValueEnum, IntValue, PointerValue}}; + +use crate::{ctx::IRContext, refs::IRValueRef, types::typing::IRType, values::IRValue}; + +#[derive(Clone)] +pub struct IRPointer<'a> { + pub inkwell_ptr: PointerValue<'a>, // Only use this directly within structs + pub t: &'a IRType<'a>, + pub name: String +} + +impl<'a> IRPointer<'a> { + pub fn new(ptr: PointerValue<'a>, t: &'a IRType<'a>, name: String) -> Self { + return IRPointer { inkwell_ptr: ptr, name, t } + } + + pub fn create(ctx: &'a IRContext<'a>, name: String, t: &'a IRType<'a>, initial: Option>) -> PositionlessResult { + let ptr = match ctx.builder.build_alloca(t.get_inkwell_basetype()?, &name) { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_alloca failed!")) + }; + + if initial.is_some() { + match ctx.builder.build_store(ptr, initial.unwrap().obtain(ctx)?.obtain()) { + Err(_) => return Err(PositionlessError::new("build_store failed!")), + Ok(_) => {} + }; + } + + return Ok(IRPointer { inkwell_ptr: ptr, t, name: name.clone() }); + } + + pub fn load(&self, ctx: &'a IRContext<'a>, t: &'a IRType<'a>) -> PositionlessResult> { + if !self.t.is_same(t) { + return Err(PositionlessError::new("Provided IRType isn't the same!")); + } + + match ctx.builder.build_load(self.t.get_inkwell_instance_basetype(ctx)?, self.inkwell_ptr, &self.name) { + Ok(v) => return Ok(IRValue::new(v, t)), + Err(_) => return Err(PositionlessError::new("build_load failed!")) + } + } + + pub fn load_from_inkwell_type(&self, ctx: &'a IRContext<'a>, t: BasicTypeEnum<'a>) -> PositionlessResult> { + if self.t.get_inkwell_instance_basetype(ctx)? != t { + return Err(PositionlessError::new("Given types aren't the same!")) + } + + match ctx.builder.build_load(t, self.inkwell_ptr, &self.name) { + Ok(v) => return Ok(IRValue::new(v, self.t)), + Err(_) => return Err(PositionlessError::new("build_load failed!")) + } + } + + pub fn store>(&self, builder: &Builder<'a>, val: V) -> bool { + return match builder.build_store(self.inkwell_ptr, val) { + Ok(_) => true, + Err(_) => false + } + } + +} \ No newline at end of file diff --git a/ir/src/irstruct/staticvars.rs b/ir/src/irstruct/staticvars.rs new file mode 100644 index 0000000..ee8f446 --- /dev/null +++ b/ir/src/irstruct/staticvars.rs @@ -0,0 +1,50 @@ +//! Static variable related code + +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::{builder::Builder, values::{BasicValueEnum, GlobalValue}}; + +use crate::{types::typing::IRType, values::IRValue}; + +#[derive(Clone)] +pub struct IRStaticVariable<'a> { + inkwell: Option>, + val: Option>, + pub t: &'a IRType<'a>, + pub name: String +} + +impl<'a> IRStaticVariable<'a> { + pub fn from_str(ctx: &'a Builder<'a>, str: &str, name: String, t: &'a IRType<'a>) -> PositionlessResult> { + let inkwell = match ctx.build_global_string_ptr(str, &name) { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_global_string_ptr failed!!")) + }; + + return Ok(IRStaticVariable { inkwell: Some(inkwell), t, name, val: None }); + } + + pub fn from_val(name: String, t: &'a IRType<'a>, val: IRValue<'a>) -> PositionlessResult> { + return Ok(IRStaticVariable { val: Some(val.obtain()), inkwell: None, t, name }) + } + + pub fn is_compiletime_replaceable(&self) -> bool { + return self.val.is_some(); + } + + pub fn as_val(&self) -> PositionlessResult> { + if self.val.is_some() { + return Ok(self.val.unwrap()); + } + + return Ok(self.as_string_ref()?.as_pointer_value().into()) + } + + pub fn as_string_ref(&self) -> PositionlessResult> { + if self.is_compiletime_replaceable() { + return Err(PositionlessError::new("Tried using as_string_ref on a compiletime determined global var")); + } + + return Ok(self.inkwell.unwrap()) + } + +} \ No newline at end of file diff --git a/ir/src/irstruct/structs.rs b/ir/src/irstruct/structs.rs new file mode 100644 index 0000000..b22825e --- /dev/null +++ b/ir/src/irstruct/structs.rs @@ -0,0 +1,105 @@ +//! IR representation of structure types (structs, layouts...) + +use commons::{err::{PositionlessError, PositionlessResult}, utils::map::HashedMap}; +use inkwell::{types::{BasicTypeEnum, StructType}}; + +use crate::{ctx::IRContext, irstruct::{funcs::IRFunction, ptr::IRPointer}, types::typing::IRType}; + +pub struct IRStructuredType<'a> { + pub inkwell_type: StructType<'a>, + pub field_to_index: HashedMap, + pub field_types: Vec<&'a IRType<'a>>, + pub functions: HashedMap>, + pub name: String, + pub is_layout: bool +} + +impl<'a> IRStructuredType<'a> { + pub fn new(ctx: &'a IRContext<'a>, name: String, layout: bool, fields: Vec<(u64, &'a IRType<'a>)>) -> PositionlessResult { + let mut map = HashedMap::new(fields.len()); + let mut typeVec: Vec> = Vec::new(); + let mut field_types: Vec<&'a IRType<'a>> = Vec::new(); + + let mut ind = 0; + for entry in fields { + map.put(entry.0, ind); + + field_types.push(entry.1); + typeVec.push(entry.1.get_inkwell_basetype()?.into()); + + ind += ind; + } + + let inkwell_type = ctx.inkwell_ctx.struct_type(&typeVec, !layout); + + return Ok(Self { inkwell_type, field_to_index: map, name, is_layout: layout, field_types, functions: HashedMap::new(0) }) + } + + pub fn append_function(&'a mut self, hash: u64, func: IRFunction<'a>) -> PositionlessResult { + if self.is_layout { + return Err(PositionlessError::new("Cannot declare functions inside of a layout!")); + } + + self.functions.put(hash, func); + return Ok(true); + } + + pub fn get_function(&'a self, hash: u64) -> PositionlessResult<&'a IRFunction<'a>> { + if self.is_layout { + return Err(PositionlessError::new("Cannot use typed-functions inside of a layout!")); + } + + return match self.functions.get(hash) { + Some(v) => Ok(v), + None => Err(PositionlessError::new("Function was not founc within the struct!")) + } + } + + pub fn get_pointer_for_field_index(&'a self, ctx: &'a IRContext<'a>, instance: &'a IRPointer<'a>, ind: u32) -> PositionlessResult> { + if ind >= self.field_types.len() as u32 { + return Err(PositionlessError::new("Invalid index given to get_pointer_for_field_index")); + } + + let field_ptr = match ctx.builder.build_struct_gep(self.inkwell_type, instance.inkwell_ptr, ind, "field_ptr") { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_struct_gep failed!")) + }; + + let field_type = self.field_types[ind as usize]; + + return Ok(IRPointer::new(field_ptr, field_type, String::from("__inner_field_ptr"))); + } + + pub fn get_pointer_for_field_index_noref(&'a self, ctx: &'a IRContext<'a>, instance: IRPointer<'a>, ind: u32) -> PositionlessResult> { + if ind >= self.field_types.len() as u32 { + return Err(PositionlessError::new("Invalid index given to get_pointer_for_field_index")); + } + + let field_ptr = match ctx.builder.build_struct_gep(self.inkwell_type, instance.inkwell_ptr, ind, "field_ptr") { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_struct_gep failed!")) + }; + + let field_type = self.field_types[ind as usize]; + + return Ok(IRPointer::new(field_ptr, field_type, String::from("__inner_field_ptr"))); + } + + pub fn get_pointer_for_field_noref(&'a self, ctx: &'a IRContext<'a>, instance: IRPointer<'a>, hash: u64) -> PositionlessResult> { + let k = match self.field_to_index.get(hash) { + Some(v) => *v, + None => return Err(PositionlessError::new(&format!("The given string hash {} doesn't represent any field in the struct {}", hash, self.name))) + }; + + return self.get_pointer_for_field_index_noref(ctx, instance, k); + } + + pub fn get_pointer_for_field(&'a self, ctx: &'a IRContext<'a>, instance: &'a IRPointer<'a>, hash: u64) -> PositionlessResult> { + let k = match self.field_to_index.get(hash) { + Some(v) => *v, + None => return Err(PositionlessError::new(&format!("The given string hash {} doesn't represent any field in the struct {}", hash, self.name))) + }; + + return self.get_pointer_for_field_index(ctx, instance, k); + } +} diff --git a/ir/src/lib.rs b/ir/src/lib.rs index a82af7b..3bf721e 100644 --- a/ir/src/lib.rs +++ b/ir/src/lib.rs @@ -1,4 +1,11 @@ //! Quickfall's Intermediate Representation code. Converts the AST into instruction like nodes to pass down to compilation. -pub mod types; \ No newline at end of file +pub mod types; +pub mod values; +pub mod math; +pub mod irstruct; +pub mod conv; +pub mod ctx; +pub mod refs; +pub mod bools; \ No newline at end of file diff --git a/ir/src/math.rs b/ir/src/math.rs new file mode 100644 index 0000000..a182b7e --- /dev/null +++ b/ir/src/math.rs @@ -0,0 +1,19 @@ +//! Math and arithmetic code + +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::{builder::Builder, values::IntValue}; +use lexer::toks::math::MathOperator; + +pub fn make_math_operation<'a>(builder: &Builder<'a>, a: IntValue<'a>, b: IntValue<'a>, name: String, operation: MathOperator) -> PositionlessResult> { + let res = match operation { + MathOperator::ADD => builder.build_int_add(a, b, &name), + MathOperator::SUBSTRACT => builder.build_int_sub(a, b, &name), + MathOperator::DIVIDE => builder.build_int_signed_div(a, b, &name), + MathOperator::MULTIPLY => builder.build_int_mul(a, b, &name) + }; + + match res { + Ok(v) => return Ok(v), + Err(_) => return Err(PositionlessError::new("Couldn't fabricate IR math operation!")) + }; +} \ No newline at end of file diff --git a/ir/src/refs.rs b/ir/src/refs.rs new file mode 100644 index 0000000..32b2eac --- /dev/null +++ b/ir/src/refs.rs @@ -0,0 +1,87 @@ +//! IR value reference definitions + +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::{builder::Builder, types::{AnyTypeEnum, BasicTypeEnum}, values::PointerValue}; + +use crate::{ctx::IRContext, irstruct::{ptr::IRPointer, staticvars::IRStaticVariable}, types::typing::IRType, values::IRValue}; + +pub enum IRValueRefKind<'a> { + Ptr(&'a IRType<'a>, IRPointer<'a>), + Val(IRValue<'a>), + Global(&'a IRType<'a>, IRStaticVariable<'a>) +} + +/// The IR value reference. Basically represents any value whatsoever, can handle every shape of values and is used for uniform handling. +pub struct IRValueRef<'a> { + // TODO: maybe change IRValueRef to host the fields itself rather than having to use Options + kind: IRValueRefKind<'a>, +} + +impl<'a> IRValueRef<'a> { + pub fn from_val(val: IRValue<'a>) -> Self { + return IRValueRef { kind: IRValueRefKind::Val(val) } + } + + pub fn from_static(val: IRStaticVariable<'a>) -> Self { + return IRValueRef { kind: IRValueRefKind::Global(val.t, val) } + } + + pub fn from_pointer(ptr: IRPointer<'a>) -> Self { + return IRValueRef { kind: IRValueRefKind::Ptr(ptr.t, ptr) } + } + + /// Determines if aqcuiring the values require a load instruction or any instruction at all to obtain the value from. + pub fn requires_load(&self) -> bool { + return matches!(self.kind, IRValueRefKind::Ptr(_, _)) + } + + pub fn obtain(&self, ctx: &'a IRContext<'a>) -> PositionlessResult> { + match &self.kind { + IRValueRefKind::Ptr(t, ptr) => { + ptr.load(ctx, t) + }, + + IRValueRefKind::Val(v) => Ok(IRValue::clone(v)), + + IRValueRefKind::Global(t, global) => { + Ok(IRValue::new(global.as_val()?, t)) + } + } + } + + pub fn get_type(&self) -> &'a IRType<'a> { + return match &self.kind { + IRValueRefKind::Val(v) => v.t, + IRValueRefKind::Ptr(t, _) => return *t, + IRValueRefKind::Global(t, _) => return *t + } + } + + pub fn as_pointer(&self) -> PositionlessResult> { + match &self.kind { + IRValueRefKind::Ptr(t, ptr) => return Ok(ptr.clone()), + _ => return Err(PositionlessError::new("Cannot cast said value reference as a pointer!")) + }; + } + + pub fn obtain_pointer(&self, ctx: &'a IRContext<'a>) -> PositionlessResult> { + match &self.kind { + IRValueRefKind::Ptr(_, ptr) => return Ok(ptr.inkwell_ptr), + + IRValueRefKind::Val(v) => { + let ptr = IRPointer::create(&ctx, String::from("_val_toptr"), v.t, Some(IRValueRef::from_val(IRValue::clone(v))))?; + + return Ok(ptr.inkwell_ptr); + } + + IRValueRefKind::Global(_, g) => { + if g.is_compiletime_replaceable() { + return Ok(g.as_val()?.into_pointer_value()) + } + + return Ok(g.as_string_ref()?.as_pointer_value()); + } + } + } + +} \ No newline at end of file diff --git a/ir/src/types/mod.rs b/ir/src/types/mod.rs index c9646bc..f05f3ec 100644 --- a/ir/src/types/mod.rs +++ b/ir/src/types/mod.rs @@ -12,6 +12,8 @@ pub const UNSIGNED32_TYPE_HASH: u64 = 2263434253207377038; pub const UNSIGNED64_TYPE_HASH: u64 = 9321926220436863523; pub const UNSIGNED128_TYPE_HASH: u64 = 9667664784078163512; +pub const POINTER_TYPE_HASH: u64 = 9259207781742941243; + pub const BOOL_TYPE_HASH: u64 = 10699440025065254663; pub mod typing; diff --git a/ir/src/types/storage.rs b/ir/src/types/storage.rs index 402b061..45de7cc 100644 --- a/ir/src/types/storage.rs +++ b/ir/src/types/storage.rs @@ -1,9 +1,9 @@ //! Type storage use commons::utils::map::HashedMap; -use inkwell::context::Context; +use inkwell::{AddressSpace, context::Context}; -use crate::types::{BOOL_TYPE_HASH, SIGNED8_TYPE_HASH, SIGNED16_TYPE_HASH, SIGNED32_TYPE_HASH, SIGNED64_TYPE_HASH, SIGNED128_TYPE_HASH, UNSIGNED8_TYPE_HASH, UNSIGNED16_TYPE_HASH, UNSIGNED32_TYPE_HASH, UNSIGNED64_TYPE_HASH, UNSIGNED128_TYPE_HASH, typing::IRType}; +use crate::types::{BOOL_TYPE_HASH, POINTER_TYPE_HASH, SIGNED8_TYPE_HASH, SIGNED16_TYPE_HASH, SIGNED32_TYPE_HASH, SIGNED64_TYPE_HASH, SIGNED128_TYPE_HASH, UNSIGNED8_TYPE_HASH, UNSIGNED16_TYPE_HASH, UNSIGNED32_TYPE_HASH, UNSIGNED64_TYPE_HASH, UNSIGNED128_TYPE_HASH, typing::IRType}; pub struct IRTypeStorage<'a> { map: HashedMap> @@ -25,6 +25,8 @@ impl<'a> IRTypeStorage<'a> { sto.insert(SIGNED64_TYPE_HASH, IRType::Signed64(ctx.i64_type())); sto.insert(SIGNED128_TYPE_HASH, IRType::Signed128(ctx.i128_type())); + sto.insert(POINTER_TYPE_HASH, IRType::Pointer(ctx.ptr_type(AddressSpace::from(0u16)))); + sto.insert(BOOL_TYPE_HASH, IRType::Bool(ctx.bool_type())); return sto; diff --git a/ir/src/types/typing.rs b/ir/src/types/typing.rs index 43bdb5d..43d6461 100644 --- a/ir/src/types/typing.rs +++ b/ir/src/types/typing.rs @@ -1,8 +1,11 @@ //! IR Type structures -use std::{cell::Ref, collections::HashMap}; +use std::{cell::Ref, collections::HashMap, ops::Add}; -use inkwell::types::IntType; +use commons::err::{PositionlessError, PositionlessResult}; +use inkwell::{AddressSpace, builder::Builder, context::Context, types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, PointerType, StringRadix}, values::PointerValue}; + +use crate::{ctx::IRContext, irstruct::structs::IRStructuredType, values::IRValue}; /// Types of IR variables pub enum IRType<'a> { @@ -18,13 +21,15 @@ pub enum IRType<'a> { Unsigned64(IntType<'a>), Unsigned128(IntType<'a>), + Pointer(PointerType<'a>), + Bool(IntType<'a>), - Struct(HashMap>>), // fields - Layout(HashMap>>) // fields + Struct(IRStructuredType<'a>), + Layout(IRStructuredType<'a>) } -impl IRType<'_> { +impl<'a> IRType<'a> { /// Gets the size in bits of a given IR element pub fn get_bitsize(&self) -> usize { match self { @@ -33,14 +38,15 @@ impl IRType<'_> { IRType::Signed32(_) | IRType::Unsigned32(_) => return 32, IRType::Signed64(_) | IRType::Unsigned64(_) => return 64, IRType::Signed128(_) | IRType::Unsigned128(_) => return 128, + IRType::Pointer(_) => return 64, IRType::Struct(v) => { let mut sz: usize = 0; // TODO: add bool compacting - for (_, ir_type) in v { - sz += ir_type.get_bitsize(); + for t in &v.field_types { + sz += t.get_bitsize(); } return sz; @@ -49,8 +55,8 @@ impl IRType<'_> { IRType::Layout(v) => { let mut sz: usize = 0; - for (_, ir_type) in v { - sz += ir_type.get_bitsize(); + for t in &v.field_types { + sz += t.get_bitsize(); } return sz; @@ -103,4 +109,135 @@ impl IRType<'_> { return -2_i128.pow(self.get_bitsize() as u32) - 1; } + + pub fn get_inkwell_basetype(&self) -> PositionlessResult> { + match self { + IRType::Unsigned8(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Unsigned16(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Unsigned32(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Unsigned64(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Unsigned128(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Signed8(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Signed16(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Signed32(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Signed64(v) => Ok(BasicTypeEnum::from(*v)), + IRType::Signed128(v) => Ok(BasicTypeEnum::from(*v)), + + IRType::Pointer(v) => Ok(BasicTypeEnum::from(*v)), + + IRType::Struct(a) => Ok(BasicTypeEnum::from(a.inkwell_type)), + IRType::Layout(a) => Ok(BasicTypeEnum::from(a.inkwell_type)), + + _ => Err(PositionlessError::new("Given IR type doesn't have any Inkwell type!!!")) + } + } + + pub fn get_inkwell_instance_basetype(&self, ctx: &'a IRContext<'a>) -> PositionlessResult> { + match self { + IRType::Struct(_) | IRType::Layout(_) => Ok(ctx.ptr_type.into()), + _ => self.get_inkwell_basetype() + } + } + + pub fn get_inkwell_base_metadatatype(&self) -> PositionlessResult> { + match self { + IRType::Unsigned8(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Unsigned16(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Unsigned32(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Unsigned64(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Unsigned128(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Signed8(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Signed16(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Signed32(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Signed64(v) => Ok(BasicMetadataTypeEnum::from(*v)), + IRType::Signed128(v) => Ok(BasicMetadataTypeEnum::from(*v)), + + IRType::Pointer(v) => Ok(BasicMetadataTypeEnum::from(*v)), + + IRType::Struct(a) => Ok(BasicMetadataTypeEnum::from(a.inkwell_type)), + IRType::Layout(a) => Ok(BasicMetadataTypeEnum::from(a.inkwell_type)), + + _ => Err(PositionlessError::new("Given IR type doesn't have any Inkwell type!!!")) + } + } + + pub fn get_inkwell_inttype(&self) -> PositionlessResult<&IntType<'a>> { + match self { + IRType::Unsigned8(v) => Ok(v), + IRType::Unsigned16(v) => Ok(v), + IRType::Unsigned32(v) => Ok(v), + IRType::Unsigned64(v) => Ok(v), + IRType::Unsigned128(v) => Ok(v), + IRType::Signed8(v) => Ok(v), + IRType::Signed16(v) => Ok(v), + IRType::Signed32(v) => Ok(v), + IRType::Signed64(v) => Ok(v), + IRType::Signed128(v) => Ok(v), + + _ => return Err(PositionlessError::new("get_inkwell_inttype was used with a non int typed IRType!")) + } + } + + pub fn get_inkwell_pointertype(ctx: &'a Context) -> PointerType<'a> { + return ctx.ptr_type(AddressSpace::from(0u16)); + } + + pub fn make_numeric_stackvar(&self, builder: &Builder<'a>, name: String, initial_val: IRValue<'a>) -> PositionlessResult> { + let t = *self.get_inkwell_inttype()?; + let alloca = match builder.build_alloca(t, &name) { + Ok(v) => v, + Err(_) => return Err(PositionlessError::new("build_alloca failed!!")) + }; + + let val = match initial_val.obtain_as_int(self) { + Some(v) => v, + None => return Err(PositionlessError::new("Value is incompatible with type!")) + }; + + if builder.build_store(alloca, val).is_err() { + return Err(PositionlessError::new("build_store failed!!")); + } + + return Ok(alloca); + } + + /// Checks if the given type instance is the same type as the given one without having to use `PartialEq` + pub fn is_same(&'a self, t: &'a IRType<'a>) -> bool { + return match(self, t) { + (IRType::Signed8(_), IRType::Signed8(_)) => true, + (IRType::Signed16(_), IRType::Signed16(_)) => true, + (IRType::Signed32(_), IRType::Signed32(_)) => true, + (IRType::Signed64(_), IRType::Signed64(_)) => true, + (IRType::Signed128(_), IRType::Signed128(_)) => true, + + (IRType::Unsigned8(_), IRType::Unsigned8(_)) => true, + (IRType::Unsigned16(_), IRType::Unsigned16(_)) => true, + (IRType::Unsigned32(_), IRType::Unsigned32(_)) => true, + (IRType::Unsigned64(_), IRType::Unsigned64(_)) => true, + (IRType::Unsigned128(_), IRType::Unsigned128(_)) => true, + + (IRType::Bool(_), IRType::Bool(_)) => true, + + (IRType::Struct(a), IRType::Struct(b)) => a.name == b.name && a.is_layout == b.is_layout, + (IRType::Layout(a), IRType::Layout(b)) => a.name == b.name && a.is_layout == b.is_layout, + + _ => false + } + } + + pub fn get_structured_type_descriptor(&'a self) -> PositionlessResult<&'a IRStructuredType<'a>> { + return match self { + IRType::Struct(e) => Ok(e), + IRType::Layout(e) => Ok(e), + _ => Err(PositionlessError::new("Given IRType doesn't have a structured type descriptor!")) + } + } + + pub fn has_structured_type_descriptor(&'a self) -> bool { + return match self { + IRType::Struct(_) | IRType::Layout(_) => true, + _ => false + } + } + } \ No newline at end of file diff --git a/ir/src/values.rs b/ir/src/values.rs new file mode 100644 index 0000000..b4f3ab6 --- /dev/null +++ b/ir/src/values.rs @@ -0,0 +1,84 @@ +//! IR value representation definitons + +use std::cell::{Ref, RefCell}; + +use commons::{err::{PositionlessError, PositionlessResult}}; +use inkwell::{types::StringRadix, values::{BasicValueEnum, IntValue}}; + +use crate::{irstruct::ptr::IRPointer, types::typing::IRType}; + +/// The new IR value system. Allows for a close interaction with inkwell rather than a more AST-side one. +/// # Safety +/// IRValue enforces a strict typing system for values. An instance of `IRType` is required for every gather and will fail if the provided type isn't the variable's. +#[derive(Clone)] +pub struct IRValue<'a> { + inkwell_val: BasicValueEnum<'a>, + pub t: IRType<'a>, +} + +impl<'a> IRValue<'a> { + /// Creates a new untracked instance + pub fn new(inkwell_val: BasicValueEnum<'a>, t: &IRType<'a>) -> Self { + return IRValue { inkwell_val, t: t.clone() } + } + + pub fn from_unsigned(t: &'a IRType<'a>, v: u128) -> PositionlessResult { + if !t.is_numeric_type() || t.is_signed() { + return Err(PositionlessError::new("The given type cannot be applied to make an unsigned!")); + } + + let int_type = t.get_inkwell_inttype()?; + let val = match int_type.const_int_from_string(&v.to_string(), StringRadix::Decimal) { + Some(v) => v, + None => return Err(PositionlessError::new("const_int_from_string failed!")) + }; + + return Ok(IRValue::new(val.into(), t)) + } + + pub fn from_signed(t: &'a IRType<'a>, v: i128) -> PositionlessResult { + if !t.is_numeric_type() || !t.is_signed() { + return Err(PositionlessError::new("The given type cannot be applied to make a signed!")); + } + + let int_type = t.get_inkwell_inttype()?; + let val = match int_type.const_int_from_string(&v.to_string(), StringRadix::Decimal) { + Some(v) => v, + None => return Err(PositionlessError::new("const_int_from_string failed!")) + }; + + return Ok(IRValue::new(val.into(), t)) + } + + pub fn from_bool(val: bool, t: &'a IRType<'a>) -> PositionlessResult { + let inkwell_type = match t { + IRType::Bool(ty) => ty, + _ => return Err(PositionlessError::new("from_bool got fed a non-boolean IRType instance! t != IRType::Bool!")) + }; + + return Ok(IRValue::new(inkwell_type.const_int(val as u64, false).into(), t)) + } + + /// Typeless obtain. Can be considered as an unsafe handle. Doesn't perform type checking + pub fn obtain(&self) -> BasicValueEnum<'a> { + return self.inkwell_val; + } + + /// Obtains the value as an integer value. Returns None if the value is incompatible with integers + pub fn obtain_as_int(&self, t: &'a IRType<'a>) -> Option> { + if !self.t.is_numeric_type() || !self.t.is_same(t) { + return None; + } + + return Some(self.inkwell_val.into_int_value()); + } + + /// Obtains the value as an bool value. Returns None if the value is incompatible with booleans + pub fn obtain_as_bool(&self) -> Option> { + return match self.t { + IRType::Bool(_) => Some(self.inkwell_val.into_int_value()), + _ => None + } + } + +} \ No newline at end of file diff --git a/parser/src/ast/func/decl.rs b/parser/src/ast/func/decl.rs index 5df13e1..e8ab91f 100644 --- a/parser/src/ast/func/decl.rs +++ b/parser/src/ast/func/decl.rs @@ -15,9 +15,16 @@ pub fn parse_function_declaraction(tokens: &Vec, ind: &mut usize) -> *ind += 1; + let mut retType = None; + + if tokens[*ind].is_keyword() { + retType = Some(tokens[*ind].expects_keyword()?.1); + *ind += 1; + } + tokens[*ind].expects(LexerTokenType::BracketOpen)?; let body = parse_node_body(tokens, ind)?; - return Ok(Box::new(ASTTreeNode::FunctionDeclaration { func_name: WithHash::new(function_name.0), args, body })); + return Ok(Box::new(ASTTreeNode::FunctionDeclaration { func_name: WithHash::new(function_name.0), args, body, returnType: retType })); } \ No newline at end of file diff --git a/parser/src/ast/func/mod.rs b/parser/src/ast/func/mod.rs index af1294b..b9f895b 100644 --- a/parser/src/ast/func/mod.rs +++ b/parser/src/ast/func/mod.rs @@ -3,7 +3,7 @@ use commons::err::PositionedResult; use lexer::token::{LexerToken, LexerTokenType}; -use crate::{ast::{parse_ast_node_in_body, tree::{ASTTreeNode, FunctionDeclarationArgument}}}; +use crate::ast::{parse_ast_node_in_body, parse_ast_value, tree::{ASTTreeNode, FunctionDeclarationArgument}}; pub mod decl; pub mod call; @@ -56,4 +56,12 @@ pub fn parse_function_arguments(tokens: &Vec, ind: &mut usize) -> Po tokens[*ind].expects(LexerTokenType::ParenClose)?; Ok(args) +} + +pub fn parse_function_return_statement(tokens: &Vec, ind: &mut usize) -> PositionedResult> { + *ind += 1; + + let val = parse_ast_value(tokens, ind)?; + + return Ok(Box::new(ASTTreeNode::ReturnStatement { val: Some(val) })) } \ No newline at end of file diff --git a/parser/src/ast/math.rs b/parser/src/ast/math.rs index 49961ca..5d5279c 100644 --- a/parser/src/ast/math.rs +++ b/parser/src/ast/math.rs @@ -17,4 +17,4 @@ pub fn parse_math_operation(tokens: &Vec, ind: &mut usize, original: let right_member = parse_ast_value(tokens, ind)?; return Ok(Box::new(ASTTreeNode::MathResult { lval: original, rval: right_member, operator: oper.0, assigns: oper.1 })) -} \ No newline at end of file +} diff --git a/parser/src/ast/mod.rs b/parser/src/ast/mod.rs index 9bf9a25..c357439 100644 --- a/parser/src/ast/mod.rs +++ b/parser/src/ast/mod.rs @@ -21,7 +21,7 @@ use commons::err::PositionedResult; use lexer::token::{LexerToken, LexerTokenType}; use utils::hash::WithHash; -use crate::{ast::{control::{forloop::parse_for_loop, ifelse::parse_if_statement, whileblock::parse_while_block}, func::{call::parse_function_call, decl::parse_function_declaraction}, literals::{parse_integer_literal, parse_string_literal}, math::parse_math_operation, tree::ASTTreeNode, types::parse_type_declaration, var::decl::parse_variable_declaration}}; +use crate::ast::{control::{forloop::parse_for_loop, ifelse::parse_if_statement, whileblock::parse_while_block}, func::{call::parse_function_call, decl::parse_function_declaraction, parse_function_return_statement}, literals::{parse_integer_literal, parse_string_literal}, math::parse_math_operation, tree::ASTTreeNode, types::parse_type_declaration, var::decl::parse_variable_declaration}; pub mod tree; pub mod func; @@ -228,6 +228,10 @@ pub fn parse_ast_node_in_body(tokens: &Vec, ind: &mut usize) -> Posi LexerTokenType::For => { return parse_for_loop(tokens, ind); + }, + + LexerTokenType::Return => { + return parse_function_return_statement(tokens, ind); } LexerTokenType::KEYWORD(str, _) => { diff --git a/parser/src/ast/tree.rs b/parser/src/ast/tree.rs index 804577d..d3e4223 100644 --- a/parser/src/ast/tree.rs +++ b/parser/src/ast/tree.rs @@ -5,8 +5,6 @@ use lexer::toks::{comp::ComparingOperator, math::MathOperator}; use utils::hash::{TypeHash, WithHash}; -use crate::ast::func; - #[derive(Debug, PartialEq, Clone)] pub struct FunctionDeclarationArgument { pub name: WithHash, @@ -43,13 +41,13 @@ pub enum ASTTreeNode { IfElseStatement { cond: Option>, body: Vec>, else_statement: Option> }, ElseStatement { body: Vec> }, + ReturnStatement { val: Option> }, + WhileBlock { cond: Box, body: Vec> }, ForBlock { initial_state: Box, cond: Box, increment: Box, body: Vec> }, - Return { value: Option> }, - FunctionCall { func: WithHash, args: Vec> }, - FunctionDeclaration { func_name: WithHash, args: Vec, body: Vec> }, + FunctionDeclaration { func_name: WithHash, args: Vec, body: Vec>, returnType: Option }, StructLRVariable { l: Box, r: Box,}, StructLRFunction { l: Box, r: Box, } @@ -70,7 +68,7 @@ impl ASTTreeNode { pub fn get_tree_name(&self) -> Option> { match self { - ASTTreeNode::FunctionDeclaration { func_name, args, body } => { + ASTTreeNode::FunctionDeclaration { func_name, args, body, returnType } => { return Some(WithHash::new(func_name.val.to_string())); }, diff --git a/test-main/Cargo.toml b/test-main/Cargo.toml index 3ed57f7..9aae5a2 100644 --- a/test-main/Cargo.toml +++ b/test-main/Cargo.toml @@ -5,4 +5,6 @@ edition = "2024" [dependencies] lexer = { path = "../lexer" } -parser = { path = "../parser" } \ No newline at end of file +parser = { path = "../parser" } +ir = { path = "../ir" } +inkwell = { version = "0.8.0", features = ["llvm21-1"]} diff --git a/test-main/src/main.rs b/test-main/src/main.rs index 8119647..7b702c2 100644 --- a/test-main/src/main.rs +++ b/test-main/src/main.rs @@ -1,7 +1,10 @@ -use std::{env}; +use core::fmt; +use std::{env, hash::{DefaultHasher, Hash, Hasher}}; +use inkwell::{context::Context, module::Module}; +use ir::{ctx::IRContext, irstruct::{funcs::IRFunction, ptr::IRPointer, staticvars::IRStaticVariable, structs::IRStructuredType}, refs::IRValueRef, types::{POINTER_TYPE_HASH, SIGNED32_TYPE_HASH, UNSIGNED32_TYPE_HASH, storage::IRTypeStorage, typing::IRType}, values::IRValue}; use lexer::lexer::lexer_parse_file; -use parser::{ast::parse_ast_node, parse_ast_ctx}; +use parser::{ast::func, parse_ast_ctx}; fn main() { let args: Vec = env::args().collect(); @@ -13,4 +16,59 @@ fn main() { println!("{:#?}", ctx); + let context = Context::create(); + + let mut storage = IRTypeStorage::new(&context); + + let module= context.create_module("main_module"); + let builder = context.create_builder(); + + let irctx = IRContext::new(builder, &context); + + let t = storage.get(UNSIGNED32_TYPE_HASH).unwrap(); + let int_type = storage.get(SIGNED32_TYPE_HASH).unwrap(); + let ptr_type = storage.get(POINTER_TYPE_HASH).unwrap(); + + let printf_func = IRFunction::create_shadow(&irctx, String::from("printf"), &module, Some(int_type), vec![ptr_type]).unwrap(); + + + let sample_substruct = IRType::Struct( + IRStructuredType::new(&irctx, String::from("uwuStruct"), true, vec![(8417845746417243860, int_type)]).unwrap() + ); + + let sample_struct = IRType::Struct( + IRStructuredType::new(&irctx, String::from("myTestStruct"), true, vec![(8417845746417243860, &sample_substruct)]).unwrap() + ); + + //storage.insert(15869126390205824132, sample_struct); + + + let i32_type = context.i32_type(); + + let func = IRFunction::create(&irctx, String::from("main"), &module, Some(t), vec![t, t]).expect("Couldn't make IR function"); + func.prepare_body_filling(&irctx.builder); + + let fmt_str = IRStaticVariable::from_str(&irctx.builder, "Haiiiii, the value is %d\n", String::from("fmt_str"), int_type).unwrap(); + //let fmt_str = &irctx.builder.build_global_string_ptr("Haiiiii, the value is %d\n", "fmt_str").unwrap(); + + // Struct test + let structInstance = IRPointer::create(&irctx, String::from("test"), &sample_struct, None).unwrap(); + + let subStructPtr = sample_struct.get_structured_type_descriptor().unwrap().get_pointer_for_field_index(&irctx, &structInstance, 0).unwrap(); + let subStructVarPtr = sample_substruct.get_structured_type_descriptor().unwrap().get_pointer_for_field_index(&irctx, &subStructPtr, 0).unwrap(); + + subStructVarPtr.store(&irctx.builder, int_type.get_inkwell_inttype().unwrap().const_int(1288, false)); + + let val = subStructVarPtr.load(&irctx, int_type).unwrap().obtain(); + + //firstFieldPointer.store(&irctx.builder, int_type.get_inkwell_inttype().unwrap().const_int(125, false)); + + // End struct test + + printf_func.call(&irctx, vec![IRValueRef::from_static(fmt_str), IRValueRef::from_pointer(structInstance)], false); + + irctx.builder.build_return(Some(&i32_type.const_zero())); + + module.print_to_file("output.ll").unwrap(); + } diff --git a/utils/src/hash.rs b/utils/src/hash.rs index bac5f7a..1acdc0d 100644 --- a/utils/src/hash.rs +++ b/utils/src/hash.rs @@ -14,7 +14,7 @@ pub type TypeHash = u64; #[derive(Debug, PartialEq, Clone)] pub struct WithHash { pub val: K, - hash: u64 + pub hash: u64 } impl WithHash {