From a18e1a94d53f65ff825c5f8ebf6db370ce5c0472 Mon Sep 17 00:00:00 2001 From: Ramesh Poudel Date: Thu, 31 Oct 2024 22:59:58 +0545 Subject: [PATCH] separate type checker and new register manager parser used to perform all the type checks. now, a new module called 'analyzer' has been added for the type checking purpose. Also, the register manager has been rewritten specifically targetting Aarch64 platform --- src/ast/ast_node.rs | 26 +- src/ast/expr.rs | 68 +++- src/ast/stmt.rs | 2 +- src/code_gen/aarch64/aarch64_codegen.rs | 499 ++++++++++++------------ src/code_gen/aarch64/aarch64_reg.rs | 276 +++++++++++++ src/code_gen/aarch64/mod.rs | 4 +- src/code_gen/cg_error.rs | 15 +- src/code_gen/codegen.rs | 73 ++-- src/code_gen/mod.rs | 4 +- src/code_gen/reg/mod.rs | 5 + src/code_gen/reg/reg_error.rs | 1 + src/code_gen/reg/register.rs | 75 ++++ src/code_gen/register.rs | 165 -------- src/error.rs | 38 +- src/main.rs | 58 +-- src/parser/parse_error.rs | 5 +- src/parser/parser_impl.rs | 296 +++++++------- src/semantic/analyzer.rs | 314 +++++++++++++++ src/semantic/mod.rs | 28 ++ src/semantic/sa_errors.rs | 45 +++ src/semantic/sa_types.rs | 3 + src/semantic/type_checker.rs | 98 +++++ src/symbol/function.rs | 9 +- src/symbol/symbol_table.rs | 26 ++ src/tokenizer/token_kind.rs | 10 + src/tokenizer/tokenizer_impl.rs | 21 +- src/types.rs | 40 +- 27 files changed, 1509 insertions(+), 695 deletions(-) create mode 100644 src/code_gen/aarch64/aarch64_reg.rs create mode 100644 src/code_gen/reg/mod.rs create mode 100644 src/code_gen/reg/reg_error.rs create mode 100644 src/code_gen/reg/register.rs delete mode 100644 src/code_gen/register.rs create mode 100644 src/semantic/analyzer.rs create mode 100644 src/semantic/mod.rs create mode 100644 src/semantic/sa_errors.rs create mode 100644 src/semantic/sa_types.rs create mode 100644 src/semantic/type_checker.rs diff --git a/src/ast/ast_node.rs b/src/ast/ast_node.rs index 6b9ad67..ef3547b 100644 --- a/src/ast/ast_node.rs +++ b/src/ast/ast_node.rs @@ -24,7 +24,7 @@ SOFTWARE. #![allow(non_camel_case_types)] -use crate::{tokenizer::TokenKind, types::{BTypeComparable, LitTypeVariant, TypeSized}}; +use crate::{tokenizer::{Token, TokenKind}, types::{BTypeComparable, LitTypeVariant, TypeSized}}; use super::ASTKind; @@ -126,7 +126,9 @@ pub struct AST { pub left: Option>, pub mid: Option>, pub right: Option>, - pub result_type: LitTypeVariant + pub result_type: LitTypeVariant, + pub start_token: Option, + pub end_token: Option } impl AST { @@ -137,7 +139,9 @@ impl AST { left: None, right: None, mid: None, - result_type: LitTypeVariant::None + result_type: LitTypeVariant::None, + start_token: None, + end_token: None } } @@ -154,14 +158,18 @@ impl AST { left: left.map(Box::new), mid: None, right: right.map(Box::new), - result_type + result_type, + start_token: None, + end_token: None } } pub fn create_leaf( kind: ASTKind, operation: ASTOperation, - result_type: LitTypeVariant + result_type: LitTypeVariant, + start_tok: Option, + end_tok: Option ) -> Self { Self { kind, @@ -169,7 +177,9 @@ impl AST { left: None, mid: None, right: None, - result_type + result_type, + start_token: start_tok, + end_token: end_tok } } @@ -187,7 +197,9 @@ impl AST { left: left.map(Box::new), mid: mid.map(Box::new), right: right.map(Box::new), - result_type + result_type, + start_token: None, + end_token: None } } } diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 3c45307..3a56424 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -1,6 +1,9 @@ use core::panic; +use std::collections::HashMap; -use crate::{error::BTypeErr, types::{are_compatible_for_operation, LitType, LitTypeVariant}}; +use lazy_static::lazy_static; + +use crate::{error::BTypeErr, semantic::{sa_errors::{SAError, SATypeError}, type_checker::TypeChecker}, types::{are_compatible_for_operation, LitType, LitTypeVariant}}; use super::{ASTOperation, AST}; @@ -109,4 +112,67 @@ impl Expr { _ => panic!() } } +} + +lazy_static! { + static ref TYPE_PRECEDENCE_EXPR: std::collections::HashMap = { + let mut typ: std::collections::HashMap = HashMap::new(); + typ.insert(LitTypeVariant::I64 as u8, 3); + typ.insert(LitTypeVariant::I32 as u8, 2); + typ.insert(LitTypeVariant::I16 as u8, 1); + typ.insert(LitTypeVariant::U8 as u8, 0); + typ + }; +} + +impl TypeChecker { + pub fn infer_type(expr: &Expr) -> Result { + return match expr { + Expr::LitVal(lit_val_expr) => Ok(lit_val_expr.result_type), + Expr::Ident(ident_expr) => Ok(ident_expr.result_type), + Expr::FuncCall(func_call_expr) => Ok(func_call_expr.result_type), + Expr::Binary(bin_expr) => { + let left_type: LitTypeVariant = TypeChecker::infer_type(&bin_expr.left)?; + let right_type: LitTypeVariant = TypeChecker::infer_type(&bin_expr.right)?; + for typ in [&left_type, &right_type] { + match typ { + LitTypeVariant::Str + | LitTypeVariant::Array + | LitTypeVariant::Null + | LitTypeVariant::Void => { + return Err( + SAError::TypeError(SATypeError::IncompatibleTypes { + a: left_type, + b: right_type, + operation: bin_expr.operation + }) + ) + }, + _ => () + }; + } + if left_type != right_type { + let lprec: Option<&u8> = TYPE_PRECEDENCE_EXPR.get(&(left_type as u8)); + let rprec: Option<&u8> = TYPE_PRECEDENCE_EXPR.get(&(right_type as u8)); + let lp: u8 = if let Some(lp) = lprec { + *lp + } else { + panic!("Type precedence not defined for operation {:?}", left_type); + }; + let rp: u8 = if let Some(rp) = rprec { + *rp + } else { + panic!("Type precedence not defined for operation {:?}", right_type); + }; + if lp > rp { + return Ok(left_type); + } else { + return Ok(right_type); + } + } + Ok(left_type) + } + _ => Err(SAError::None) + }; + } } \ No newline at end of file diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index dbd5a83..00aa509 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -1,4 +1,4 @@ -use crate::{types::LitType, StorageClass}; +use crate::StorageClass; use super::Expr; diff --git a/src/code_gen/aarch64/aarch64_codegen.rs b/src/code_gen/aarch64/aarch64_codegen.rs index 291ffbd..0e579cd 100644 --- a/src/code_gen/aarch64/aarch64_codegen.rs +++ b/src/code_gen/aarch64/aarch64_codegen.rs @@ -34,11 +34,13 @@ use crate::ast::{ ASTOperation, AST }; +use crate::code_gen::aarch64::aarch64_reg::REG_64BIT; use crate::code_gen::codegen::CodeGenResult; -use crate::code_gen::codegen::EARLY_RETURN; use crate::code_gen::codegen::NO_REG; +use crate::code_gen::reg::AllocedReg; +use crate::code_gen::reg::RegAllocResult; +use crate::code_gen::reg::RegManager; use crate::code_gen::CodeGen; -use crate::code_gen::register::RegManager; use crate::code_gen::CodeGenErr; use crate::context::CompilerCtx; use crate::symbol::{ @@ -55,15 +57,26 @@ use crate::utils::integer::i64_hex_split_quarter; use crate::utils::integer::to_hex; use crate::SymbolType; +use super::aarch64_reg::Aarch64RegManager; + lazy_static::lazy_static! { static ref CMP_CONDS_LIST: Vec<&'static str> = vec!["ne", "eq", "ge", "le", "lt", "gt"]; } pub struct Aarch64CodeGen<'aarch64> { - reg_manager: RefCell, + reg_manager: RefCell, + ctx: Option>>>, + + // label ID tracker label_id: usize, + + /// current function's ID function_id: usize, + + /// Current function that is being parsed + current_function: Option, + early_return_label_id: usize } @@ -113,7 +126,7 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { let label_end: usize = self.get_next_label(); // Evaluate the condition and store the result in a register - let _cond_result_reg: usize = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_if_false, ASTOperation::AST_IF)?; + let _cond_result_reg: AllocedReg = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_if_false, ASTOperation::AST_IF)?; // Free registers to allow usage within the if-else body self.reg_manager.borrow_mut().deallocate_all(); @@ -138,12 +151,12 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { self.reg_manager.borrow_mut().deallocate_all(); _ = self.gen_label(label_end)?; // Mark the end of the if-else block } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } - fn gen_cmp_and_set(&self, operation: ASTOperation, r1: usize, r2: usize) -> CodeGenResult { - let r1name: String = self.reg_manager.borrow().name(r1, 0); - let r2name: String = self.reg_manager.borrow().name(r2, 0); + fn gen_cmp_and_set(&self, operation: ASTOperation, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult { + let r1name: String = self.reg_manager.borrow().name(r1.idx, &r1.lit_type()); + let r2name: String = self.reg_manager.borrow().name(r2.idx, &r2.lit_type()); println!("cmp {}, {}", r1name, r2name); let compare_operator: &str = match operation { ASTOperation::AST_LTHAN => "bge", @@ -163,9 +176,9 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { Ok(r2) } - fn gen_cmp_and_jmp(&self, operation: ASTOperation, r1: usize, r2: usize, label: usize) -> CodeGenResult { - let r1name: String = self.reg_manager.borrow().name(r1, 0); - let r2name: String = self.reg_manager.borrow().name(r2, 0); + fn gen_cmp_and_jmp(&self, operation: ASTOperation, r1: AllocedReg, r2: AllocedReg, label: usize) -> CodeGenResult { + let r1name: String = self.reg_manager.borrow().name(r1.idx, &r1.lit_type()); + let r2name: String = self.reg_manager.borrow().name(r2.idx, &r2.lit_type()); println!("cmp {}, {}", r1name, r2name); let compare_operator: &str = match operation { ASTOperation::AST_LTHAN => "bhs", @@ -182,7 +195,7 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { label ); self.reg_manager.borrow_mut().deallocate_all(); - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_function_stmt(&mut self, ast: &AST) -> CodeGenResult { @@ -191,25 +204,28 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { Stmt::FuncDecl(func_decl) => func_decl.func_id, _ => panic!("Not a valid symbol table indexing method"), }; + let func_name: String = if let Some(ctx_rc) = &mut self.ctx { let ctx_borrow: std::cell::Ref> = ctx_rc.borrow(); - ctx_borrow.sym_table.get_symbol(index).unwrap().name.clone() - } else { - return Err(CodeGenErr::NoContext); - }; - let func_info: FunctionInfo = if let Some(ctx_rc) = &mut self.ctx { - let ctx_borrow = ctx_rc.borrow(); - ctx_borrow.func_table.get(&func_name).unwrap().clone() + let func_name: String = ctx_borrow.sym_table.get_symbol(index).unwrap().name.clone(); + if let Some(finfo) = ctx_borrow.func_table.get(&func_name) { + self.current_function = Some(finfo.clone()); + func_name + } else { + panic!("Function not found"); + } } else { return Err(CodeGenErr::NoContext); }; + let func_info: FunctionInfo = self.current_function.as_ref().cloned().unwrap(); + // If the function is declared as extern, print its external linkage // declaration and return a placeholder value indicating an unresolved // address (0xFFFFFFFF). if func_info.storage_class == StorageClass::EXTERN { println!(".extern _{}", func_name); - return Ok(NO_REG); + return Ok(AllocedReg::no_reg()); } // Function preamble @@ -223,8 +239,8 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { // generating code for function parameters for param_sym in func_info.params.iter() { if param_sym.lit_type != LitTypeVariant::None { - let param_reg = self.reg_manager.borrow_mut().allocate_param_reg(); - println!("str {}, [x29, #-{}]", self.reg_manager.borrow().name(param_reg, 0), param_sym.offset); + let param_reg: AllocedReg = self.__allocate_reg(¶m_sym.lit_type); // .reg_manager.borrow_mut().allocate_param_reg(¶m_sym.lit_type); + println!("str {}, [x29, #-{}]", self.reg_manager.borrow().name(param_reg.idx, ¶m_sym.lit_type), param_sym.offset); } } @@ -240,24 +256,30 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { self.early_return_label_id = NO_REG; // reset early return label after function code generation } } + + self.current_function = None; + // ldp -> Load Pair of Registers // Restore the saved frame pointer (x29) and link register (x30) from the stack. println!("ldp x29, x30, [sp], 0x10"); println!("add sp, sp, #{}\nret", func_info.stack_size + 16); - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_while_stmt(&mut self, ast: &AST) -> CodeGenResult { let label_start: usize = self.get_next_label(); let label_end: usize = self.get_next_label(); _ = self.gen_label(label_start)?; // start of loop body - _ = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_end, ast.operation)?; - self.reg_manager.borrow_mut().deallocate_all(); - _ = self.gen_code_from_ast(ast.right.as_ref().unwrap(), label_end, ast.operation)?; - self.reg_manager.borrow_mut().deallocate_all(); + + let cond_res_reg: AllocedReg = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_end, ast.operation)?; + self.reg_manager.borrow_mut().deallocate(cond_res_reg.idx, &cond_res_reg.lit_type()); + + let body_reg: AllocedReg = self.gen_code_from_ast(ast.right.as_ref().unwrap(), label_end, ast.operation)?; + self.reg_manager.borrow_mut().deallocate(body_reg.idx, &body_reg.lit_type()); + _ = self.gen_jump(label_start)?; _ = self.gen_label(label_end)?; - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_loop_stmt(&mut self, ast: &AST) -> CodeGenResult { @@ -268,39 +290,39 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { let label_end: usize = self.get_next_label(); _ = self.gen_label(label_start)?; - _ = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_end, ast.operation)?; - self.reg_manager.borrow_mut().deallocate_all(); + + let body_reg: AllocedReg = self.gen_code_from_ast(ast.left.as_ref().unwrap(), label_end, ast.operation)?; + self.reg_manager.borrow_mut().deallocate(body_reg.idx, &body_reg.lit_type()); + _ = self.gen_jump(label_start)?; _ = self.gen_label(label_end)?; - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_break_stmt(&mut self, break_label: usize) -> CodeGenResult { println!("b _L{}", break_label); - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_load_id_into_reg(&mut self, id: usize) -> CodeGenResult { - let mut reg: usize = 0xFFFFFFFF; - let inside_func: bool = self.function_id != reg; - if inside_func { - reg = self.reg_manager.borrow_mut().allocate_param_reg(); + let symbol: Symbol = self.find_symbol_glob_or_loc(id)?; + let val_type: &LitTypeVariant = &symbol.lit_type; + + let mut reg: AllocedReg = AllocedReg::no_reg(); + let calling_func: bool = self.function_id != reg.idx; + if calling_func { + reg = self.__allocate_reg(val_type); // self.reg_manager.borrow_mut().allocate_param_reg(val_type); } else { - reg = self.reg_manager.borrow_mut().allocate(); + reg = self.__allocate_reg(val_type); // self.reg_manager.borrow_mut().allocate(val_type); } - let value_reg_name: String = self.reg_manager.borrow().name(reg, 0); - let symbol: Symbol = if let Some(ctx_rc) = &mut self.ctx { - let ctx_borrow = ctx_rc.borrow(); - ctx_borrow.sym_table.get_symbol(id).unwrap().clone() - } else { - return Err(CodeGenErr::NoContext); - }; + let value_reg_name: String = self.reg_manager.borrow().name(reg.idx, ®.lit_type()); + if symbol.class == StorageClass::GLOBAL { - let reg: usize = self.reg_manager.borrow_mut().allocate(); - let reg_name: String = self.reg_manager.borrow().name(reg, 0); + let value_reg: AllocedReg = self.__allocate_reg(val_type); // self.reg_manager.borrow_mut().allocate(); + let reg_name: String = self.reg_manager.borrow().name(value_reg.idx, &value_reg.lit_type()); self.dump_gid_address_load_code_from_name(®_name, id); println!("ldr {}, [{}] // load {}", value_reg_name, reg_name, symbol.name); - self.reg_manager.borrow_mut().deallocate(reg); + self.reg_manager.borrow_mut().deallocate(value_reg.idx, &value_reg.lit_type()); } else { println!("ldr {}, [x29, #-{}] // load {}", value_reg_name, symbol.local_offset, symbol.name); } @@ -308,49 +330,37 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { } // Refer to this page for explanation on '@PAGE' and '@PAGEOFF': https://stackoverflow.com/questions/65351533/apple-clang12-llvm-unknown-aarch64-fixup-kind - fn gen_store_reg_value_into_id(&mut self, reg: usize, id: usize) -> CodeGenResult { - let reg_name: String = self.reg_manager.borrow().name(reg, 0); - let symbol: Symbol = if let Some(ctx_rc) = &mut self.ctx { - let ctx_borrow = ctx_rc.borrow(); - ctx_borrow.sym_table.get_symbol(id).unwrap().clone() - } else { - return Err(CodeGenErr::NoContext); - }; - if symbol.class == StorageClass::GLOBAL { - let addr_reg: usize = self.reg_manager.borrow_mut().allocate(); - let addr_reg_name: String = self.reg_manager.borrow().name(addr_reg, 0); + fn gen_store_reg_value_into_id(&mut self, reg: AllocedReg, id: usize) -> CodeGenResult { + let reg_name: String = self.reg_manager.borrow().name(reg.idx, ®.lit_type()); + + let symbol: Symbol = self.find_symbol_glob_or_loc(id)?; + + let addr_reg: AllocedReg = if symbol.class == StorageClass::GLOBAL { + let ar: AllocedReg = self.__allocate_reg(&symbol.lit_type); + let addr_reg_name: String = self.reg_manager.borrow().name(ar.idx, &ar.lit_type()); self.dump_gid_address_load_code_from_name(&addr_reg_name, id); println!("str {}, [{}] // store into {}", reg_name, addr_reg_name, symbol.name); - Ok(addr_reg) + ar } else { println!("str {}, [x29, #-{}] // store into {}", reg_name, symbol.local_offset, symbol.name); - Ok(NO_REG) - } + AllocedReg::no_reg() + }; + self.reg_manager.borrow_mut().deallocate(reg.idx, ®.lit_type()); + Ok(addr_reg) } // Load an integer literal into a register fn gen_load_intlit_into_reg(&mut self, value: &LitType) -> CodeGenResult { - let mut reg: usize = 0xFFFFFFFF; - let mut reg_off: usize = 0x0; - let inside_func: bool = self.function_id != reg && reg_off == 0; + let mut reg: AllocedReg = AllocedReg::no_reg(); + let inside_func: bool = self.function_id != reg.idx; if inside_func { - reg_off = match value { - LitType::I32(_) - | LitType::I16(_) - | LitType::U8(_) => 8, - _ => 0 - }; - reg = self.reg_manager.borrow_mut().allocate_param_reg(); + reg = self.__allocate_reg(&value.variant()); // self.reg_manager.borrow_mut().allocate_param_reg(); + self.reg_manager.borrow_mut().deallocate_param_reg(reg.idx, ®.lit_type()); } else { - reg_off = match value { - LitType::I32(_) - | LitType::I16(_) - | LitType::U8(_) => 29, - _ => 0 - }; - reg = self.reg_manager.borrow_mut().allocate(); + reg = self.__allocate_param_reg(&value.variant()); // self.reg_manager.borrow_mut().allocate(); + self.reg_manager.borrow_mut().deallocate(reg.idx, ®.lit_type()); } - let reg_name: String = self.reg_manager.borrow().name(reg, reg_off); + let reg_name: String = self.reg_manager.borrow().name(reg.idx, ®.lit_type()); let result: Result = Aarch64CodeGen::gen_int_value_load_code(value, ®_name); if let Ok(code) = result { println!("{}", code); @@ -362,48 +372,48 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { // id --> label information of the string fn gen_load_global_strlit(&mut self, id: &LitType) -> CodeGenResult { - let mut reg: usize = 0xFFFFFFFF; - let inside_func: bool = self.function_id != reg; + let mut reg: AllocedReg = AllocedReg::no_reg(); + let inside_func: bool = self.function_id != reg.idx; if inside_func { - reg = self.reg_manager.borrow_mut().allocate_param_reg(); + reg = self.__allocate_param_reg(&id.variant()); // self.reg_manager.borrow_mut().allocate_param_reg(); } else { - reg = self.reg_manager.borrow_mut().allocate(); + reg = self.__allocate_reg(&id.variant()); // self.reg_manager.borrow_mut().allocate(); } - let str_addr_name: String = self.reg_manager.borrow().name(reg, 0); + let str_addr_name: String = self.reg_manager.borrow().name(reg.idx, ®.lit_type()); self.dump_gid_address_load_code_from_label_id(&str_addr_name, id); Ok(reg) } - fn gen_add(&mut self, r1: usize, r2: usize) -> CodeGenResult { + fn gen_add(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult { println!( "add {}, {}, {}", - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r2, 0) + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r2.idx, &r2.lit_type()) ); - self.reg_manager.borrow_mut().deallocate(r2); + self.reg_manager.borrow_mut().deallocate(r2.idx, &r2.lit_type()); Ok(r1) } - fn gen_sub(&mut self, r1: usize, r2: usize) -> CodeGenResult { + fn gen_sub(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult { println!( "sub {}, {}, {}", - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r2, 0) + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r2.idx, &r2.lit_type()) ); - self.reg_manager.borrow_mut().deallocate(r2); + self.reg_manager.borrow_mut().deallocate(r2.idx, &r2.lit_type()); Ok(r1) } - fn gen_mul(&mut self, r1: usize, r2: usize) -> CodeGenResult { + fn gen_mul(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult { println!( "mul {}, {}, {}", - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r1, 0), - self.reg_manager.borrow().name(r2, 0) + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r1.idx, &r1.lit_type()), + self.reg_manager.borrow().name(r2.idx, &r2.lit_type()) ); - self.reg_manager.borrow_mut().deallocate(r2); + self.reg_manager.borrow_mut().deallocate(r2.idx, &r2.lit_type()); Ok(r1) } @@ -420,16 +430,19 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { } if early_return { - Ok(EARLY_RETURN) + Ok(AllocedReg::early_return()) } else { - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } } fn gen_array_access(&mut self, id: usize, expr: &AST) -> CodeGenResult { - let expr_res_reg: usize = self.gen_code_from_ast(expr, 0xFFFFFFFF, ASTOperation::AST_ARRAY_ACCESS)?; - let mut reg_mgr = self.reg_manager.borrow_mut(); - let expr_res_reg_name: String = reg_mgr.name(expr_res_reg, 0); + let expr_res_reg: AllocedReg = self.gen_code_from_ast(expr, 0xFFFFFFFF, ASTOperation::AST_ARRAY_ACCESS)?; + let expr_res_reg_name: String = self.reg_manager.borrow().name(expr_res_reg.idx, &expr_res_reg.lit_type()); + + // dealloc the expr register + self.reg_manager.borrow_mut().deallocate(expr_res_reg.idx, &expr_res_reg.lit_type()); + let symbol: Symbol = if let Some(ctx_rc) = &mut self.ctx { let ctx_borrow = ctx_rc.borrow(); ctx_borrow.sym_table.get_symbol(id).unwrap().clone() @@ -442,59 +455,38 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { // items are 32-bit _ => 0, }; + // this will contain the address + offset of an array - let addr_reg: usize = reg_mgr.allocate(); - let addr_reg_name: String = reg_mgr.name(addr_reg, 0); - let off_addr_reg: usize = reg_mgr.allocate(); - let off_addr_reg_name: String = reg_mgr.name(off_addr_reg, 0); - std::mem::drop(reg_mgr); + let addr_reg: AllocedReg = self.__allocate_reg(&LitTypeVariant::I64); // self.reg_manager.borrow_mut().allocate(&LitTypeVariant::I64); + let addr_reg_name: String = self.reg_manager.borrow().name(addr_reg.idx, &addr_reg.lit_type()); // self.reg_manager.borrow().name(addr_reg, 0); + + let off_addr_reg: AllocedReg = self.__allocate_reg(&LitTypeVariant::I64); // self.reg_manager.borrow_mut().allocate(); + let off_addr_reg_name: String = self.reg_manager.borrow().name(off_addr_reg.idx, &off_addr_reg.lit_type()); + self.dump_gid_address_load_code_from_name(&addr_reg_name, id); + println!( "ldr {}, [{}, {}, lsl {}]", off_addr_reg_name, addr_reg_name, expr_res_reg_name, offset_shift ); + + self.reg_manager.borrow_mut().deallocate(addr_reg.idx, &addr_reg.lit_type()); Ok(off_addr_reg) } - fn gen_array_access2(&mut self, symbol_id: usize, index: usize) -> CodeGenResult { - let mut reg_mgr: RefMut = self.reg_manager.borrow_mut(); - let expr_res_reg_name: String = reg_mgr.name(index, 0); - let symbol: Symbol = if let Some(ctx_rc) = &mut self.ctx { - let ctx_borrow = ctx_rc.borrow(); - ctx_borrow.sym_table.get_symbol(symbol_id).unwrap().clone() - } else { - panic!("Please provide to retrieve symbol from!"); - }; - let offset_shift: usize = match symbol.lit_type { - LitTypeVariant::I32 => 2, - LitTypeVariant::I64 => 3, - _ => 0, - }; - // this will contain the address + offset of an array - let addr_reg: usize = reg_mgr.allocate(); - let addr_reg_name: String = reg_mgr.name(addr_reg, 0); - let off_addr_reg: usize = reg_mgr.allocate(); - let off_addr_reg_name: String = reg_mgr.name(off_addr_reg, 0); - drop(reg_mgr); - self.dump_gid_address_load_code_from_name(&addr_reg_name, symbol_id); - println!( - "ldr {}, [{}, {}, lsl {}]", - off_addr_reg_name, addr_reg_name, expr_res_reg_name, offset_shift - ); - Ok(off_addr_reg) - } - + // generate 'branch' code. Not branch with link fn gen_jump(&self, label_id: usize) -> CodeGenResult { println!("b _L{}", label_id); - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } + // generate normal label code fn gen_label(&mut self, label: usize) -> CodeGenResult { println!("_L{}:", label); - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } - fn reg_manager(&self) -> RefMut { + fn reg_manager(&self) -> RefMut { self.reg_manager.borrow_mut() } @@ -516,56 +508,65 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { // Assign the function id whenever generating code for function self.function_id = func_info.func_id; let args: &Vec = &func_call_stmt.args; + let mut used_regs: Vec = Vec::::new(); for expr in args { - _ = self.gen_expr(expr, ASTOperation::AST_FUNC_CALL, 0xFFFFFFFF, ASTOperation::AST_NONE)?; - // let reg_name: String = self.reg_manager.borrow().name(reg); - // let param_reg: usize = self.reg_manager.borrow_mut().allocate_param_reg(); - // let param_reg_name: String = self.reg_manager.borrow().name(param_reg); - // println!("mov {}, {}", param_reg_name, reg_name); + let used_reg: AllocedReg = self.gen_expr(expr, ASTOperation::AST_FUNC_CALL, 0xFFFFFFFF, ASTOperation::AST_NONE)?; + used_regs.push(used_reg); + } + let mut _reg_mgr = self.reg_manager.borrow_mut(); + for ar in used_regs { + _reg_mgr.deallocate(ar.idx, &ar.lit_type()); } println!("bl _{}", func_info.name); self.function_id = 0xFFFFFFFF; - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_local_var_decl_stmt(&mut self, var_decl_stmt: &crate::ast::VarDeclStmt, expr_ast: &Expr) -> CodeGenResult { - let symbol: Symbol = if let Some(ctx_rc) = &self.ctx { - let ctx_borrow = ctx_rc.borrow(); - if let Some(symbol) = ctx_borrow.sym_table.get_symbol(var_decl_stmt.symtbl_pos) { - symbol.clone() - } else { - panic!("not possible to reach here"); - } - } else { - return Err(CodeGenErr::NoContext); - }; - let expr_reg_res: usize = self.gen_expr(expr_ast, ASTOperation::AST_VAR_DECL, 0xFFFFFFFF, ASTOperation::AST_NONE)?; - let reg_name: String = self.reg_manager.borrow().name(expr_reg_res, 0); + if self.current_function.is_none() { + panic!("Parsing a local variable but function is not defined... Weird."); + } + + let func_info: &FunctionInfo = self.current_function.as_ref().unwrap(); + let symbol: Symbol = func_info.local_syms.get_or_fail(var_decl_stmt.symtbl_pos).clone(); + + let expr_reg_res: AllocedReg = self.gen_expr(expr_ast, ASTOperation::AST_VAR_DECL, 0xFFFFFFFF, ASTOperation::AST_NONE)?; + let reg_name: String = self.reg_manager.borrow().name(expr_reg_res.idx, &expr_reg_res.lit_type()); println!("str {}, [x29, #-{}] // store into {}", reg_name, symbol.local_offset, symbol.name); - self.reg_manager.borrow_mut().deallocate_all(); - Ok(NO_REG) + self.reg_manager.borrow_mut().deallocate(expr_reg_res.idx, &expr_reg_res.lit_type()); + Ok(AllocedReg::no_reg()) } fn gen_local_arr_var_decl_stmt(&mut self, arr_var_decl_stmt: &crate::ast::ArrVarDeclStmt) -> CodeGenResult { - let symbol: Symbol = if let Some(ctx_rc) = &self.ctx { - let ctx_borrow = ctx_rc.borrow(); - if let Some(symbol) = ctx_borrow.sym_table.get_symbol(arr_var_decl_stmt.symtbl_pos) { - symbol.clone() - } else { - panic!("not possible to reach here"); - } - } else { - return Err(CodeGenErr::NoContext); - }; - let mut item_off_counter = symbol.local_offset; + if self.current_function.is_none() { + panic!("Parsing a local variable but function is not defined... Weird."); + } + + let func_info: &FunctionInfo = self.current_function.as_ref().unwrap(); + let symbol: Symbol = func_info.local_syms.get_or_fail(arr_var_decl_stmt.symtbl_pos).clone(); + + // dump array size information onto the stack + let size_reg: AllocedReg = self.gen_load_intlit_into_reg(&LitType::I32(symbol.size as i32))?; + let size_reg_name: String = self.reg_manager.borrow().name(size_reg.idx, &size_reg.lit_type()); + println!("str {}, [x29, #-{}]", size_reg_name, symbol.local_offset); + + let mut item_off_counter: i32 = symbol.local_offset + 4; // add '4' because we had to store size information for expr in &arr_var_decl_stmt.vals { - let expr_reg: usize = self.gen_expr(expr, ASTOperation::AST_ARR_VAR_DECL, NO_REG, ASTOperation::AST_NONE)?; - let reg_name: String = self.reg_manager.borrow().name(expr_reg, 0); + let expr_reg: AllocedReg = self.gen_expr(expr, ASTOperation::AST_ARR_VAR_DECL, NO_REG, ASTOperation::AST_ARR_VAR_DECL)?; + let reg_name: String = self.reg_manager.borrow().name(expr_reg.idx, &expr_reg.lit_type()); println!("str {}, [x29, #-{}]", reg_name, item_off_counter); - item_off_counter += symbol.lit_type.size() as i32; + item_off_counter += { + match symbol.lit_type { + LitTypeVariant::I64 => 8, + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => 4, + _ => panic!("cannot create offset for type: '{:?}'", symbol.lit_type) + } + }; self.reg_manager.borrow_mut().deallocate_all(); } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } fn gen_func_call_expr(&mut self, func_call_expr: &crate::ast::FuncCallExpr) -> CodeGenResult { @@ -581,45 +582,46 @@ impl<'aarch64> CodeGen for Aarch64CodeGen<'aarch64> { }; if func_info_res.is_none() { - return Err(CodeGenErr::UndefinedSymbol("function name".to_string())); + return Err(CodeGenErr::UndefinedSymbol); } - let func_info = func_info_res.unwrap(); + let func_info: FunctionInfo = func_info_res.unwrap(); let args: &Vec = &func_call_expr.args; for expr in args { _ = self.gen_expr(expr, ASTOperation::AST_FUNC_CALL, 0xFFFFFFFF, ASTOperation::AST_NONE)?; - // let reg_name: String = self.reg_manager.borrow().name(reg); - // let param_reg: usize = self.reg_manager.borrow_mut().allocate_param_reg(); - // let param_reg_name: String = self.reg_manager.borrow().name(param_reg); - // println!("mov {}, {}", param_reg_name, reg_name); } + println!("bl _{}", func_info.name); self.function_id = 0xFFFFFFFF; - Ok(0) // always return the 'x0' register's index after function calls + Ok(AllocedReg { + size: REG_64BIT, + idx: 0 + }) // always return the 'x0' register's index after function calls } fn gen_var_assignment_stmt(&mut self, assign_stmt: &crate::ast::AssignStmt, expr_ast: &Expr) -> CodeGenResult { - let expr_reg: usize = self.gen_expr(expr_ast, ASTOperation::AST_ASSIGN, 0xFFFFFFFF, ASTOperation::AST_NONE)?; + let expr_reg: AllocedReg = self.gen_expr(expr_ast, ASTOperation::AST_ASSIGN, 0xFFFFFFFF, ASTOperation::AST_NONE)?; _ = self.gen_store_reg_value_into_id(expr_reg, assign_stmt.symtbl_pos)?; - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } } impl<'aarch64> Aarch64CodeGen<'aarch64> { pub fn new( - reg_manager: RefCell, + reg_manager: RefCell, ) -> Self { Self { reg_manager, ctx: None, label_id: 0, function_id: NO_REG, - early_return_label_id: NO_REG + early_return_label_id: NO_REG, + current_function: None } } - pub fn gen_with_ctx(&mut self, ctx: Rc>>, nodes:Vec) { + pub fn gen_with_ctx(&mut self, ctx: Rc>>, nodes: &Vec) { self.label_id = ctx.borrow().label_id + 10; self.ctx = Some(ctx); self.start_gen(nodes); @@ -631,6 +633,40 @@ impl<'aarch64> Aarch64CodeGen<'aarch64> { lbl } + /// Searches for a symbol either in the local scope of the current function + /// or in the global context. + /// + /// - If `current_function` is defined, attempts to retrieve the symbol from + /// the local symbols of the current function. + /// - If `current_function` is `None`, checks for a global context (`ctx`) and + /// attempts to retrieve the symbol from the global symbol table. + /// - Returns an error if neither a local function context nor a global context + /// is available. + /// + /// # Arguments + /// * `index` - The index of the symbol in the symbol table. + /// + /// # Returns + /// * `Ok(Symbol)` - The symbol found at the specified index. + /// * `Err(CodeGenErr::NoContext)` - If neither local nor global context is available + /// for the lookup. + fn find_symbol_glob_or_loc(&self, index: usize) -> Result { + // local context is checked first + if self.current_function.is_some() { + let func_info: FunctionInfo = self.current_function.as_ref().cloned().unwrap(); + Ok(func_info.local_syms.get_or_fail(index).clone()) + } + // then comes the global + else if let Some(ctx_rc) = &self.ctx { + let ctx_borrow = ctx_rc.borrow(); + Ok(ctx_borrow.sym_table.get_symbol(index).unwrap().clone()) + } + // symbol not found + else { + return Err(CodeGenErr::UndefinedSymbol); + } + } + // ADRP loads the address of the page of given variables. In other words, // ADRP loads the address of the page where the given global identifier lies // on. Doing this alone doesn't give us the address of the global identifier. @@ -727,79 +763,26 @@ impl<'aarch64> Aarch64CodeGen<'aarch64> { }; Ok(result) } + + fn __allocate_reg(&mut self, val_type: &LitTypeVariant) -> AllocedReg { + println!("{:?}", val_type); + let alloced_reg: RegAllocResult = self.reg_manager.borrow_mut().allocate(val_type); + if alloced_reg.is_err() { + panic!("Couldn't allocate register"); + } + alloced_reg.ok().unwrap() + } + + fn __allocate_param_reg(&mut self, val_type: &LitTypeVariant) -> AllocedReg { + let alloced_reg: RegAllocResult = self.reg_manager.borrow_mut().allocate_param_reg(val_type); + if alloced_reg.is_err() { + panic!("Couldn't allocate parameter register"); + } + alloced_reg.ok().unwrap() + } } #[cfg(test)] mod tests { - use core::f64; - use crate::{code_gen::RegManager, types::LitType}; - - use super::Aarch64CodeGen; - - fn _create_reg_mgr() -> RegManager { - let rm: RegManager = RegManager::new( - { - let mut regs: Vec = vec![]; - for i in 0..=28 { - regs.push(format!("x{}", i)); - } - regs - }, - { - let mut regs: Vec = vec![]; - for i in 0..=7 { - regs.push(format!("x{}", i)); - } - regs - } - ); - rm - } - - #[test] - fn test_gen_int_value_load_code_i64() { - // let rm = create_reg_mgr(); - // let cg = Aarch64CodeGen::new(RefCell::new(rm)); - let value = LitType::I64(0x123456789ABCDEF0); - let result = Aarch64CodeGen::gen_int_value_load_code(&value, "x0").unwrap(); - let expected = - "movz x0, 0x1234, lsl #48\n\ - movk x0, 0x5678, lsl #32\n\ - movk x0, 0x9abc, lsl #16\n\ - movk x0, 0xdef0"; - assert_eq!(result, expected); - } - - #[test] - fn test_gen_int_value_load_code_i32() { - let value = LitType::I32(0x12345678); - let result = Aarch64CodeGen::gen_int_value_load_code(&value, "x0").unwrap(); - let expected = - "movz x0, 0x1234, lsl #16\n\ - movk x0, 0x5678"; - assert_eq!(result, expected); - } - - #[test] - fn test_gen_int_value_load_code_i16() { - let value = LitType::I16(0x1234); - let result = Aarch64CodeGen::gen_int_value_load_code(&value, "w0").unwrap(); - let expected = "movz w0, 0x1234"; - assert_eq!(result, expected); - } - - #[test] - fn test_gen_int_value_load_code_u8() { - let value = LitType::U8(0x12); - let result = Aarch64CodeGen::gen_int_value_load_code(&value, "w0").unwrap(); - let expected = "movz w0, 0x12"; - assert_eq!(result, expected); - } - - #[test] - fn test_gen_int_value_load_code_unsupported_type() { - let value = LitType::F64(f64::consts::PI); - let result = Aarch64CodeGen::gen_int_value_load_code(&value, "x0"); - assert!(result.is_err()); // The function should return an error for unsupported types - } + } \ No newline at end of file diff --git a/src/code_gen/aarch64/aarch64_reg.rs b/src/code_gen/aarch64/aarch64_reg.rs new file mode 100644 index 0000000..7bfbe5f --- /dev/null +++ b/src/code_gen/aarch64/aarch64_reg.rs @@ -0,0 +1,276 @@ +use std::collections::HashMap; + +use crate::{ + code_gen::{codegen::EARLY_RETURN, reg::{ + AllocedReg, + RegAllocError, + RegAllocResult, + RegIdx, + RegManager + }}, + types::LitTypeVariant +}; + +pub const REG_64BIT: usize = 64; +pub const REG_32BIT: usize = 32; + +const REG_64BIT_COUNT: usize = 28; +const REG_32BIT_COUNT: usize = 28; + +impl AllocedReg { + pub fn lit_type(&self) -> LitTypeVariant { + match self.size { + REG_32BIT => LitTypeVariant::I32, + REG_64BIT => LitTypeVariant::I64, + _ => LitTypeVariant::None + } + } + + pub fn early_return() -> Self { + AllocedReg { + idx: EARLY_RETURN, + size: 0 + } + } +} + +pub struct Aarch64RegManager { + /// 64-bit registers + regs64: HashMap, + + /// 64-bit parameter registers + param_regs64: HashMap, + + /// 32-bit registers + regs32: HashMap, + + /// 32-bit parameter registers + param_regs32: HashMap +} + +impl RegManager for Aarch64RegManager { + fn allocate(&mut self, val_type: &LitTypeVariant) -> RegAllocResult { + match val_type { + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => self.alllocate_32bit_reg(), + LitTypeVariant::I64 => self.alllocate_64bit_reg(), + _ => Err(RegAllocError) + } + } + + fn deallocate(&mut self, idx: RegIdx, val_type: &LitTypeVariant) { + match val_type { + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => self.deallocate_32bit_reg(idx), + LitTypeVariant::I64 => self.deallocate_64bit_reg(idx), + _ => () + } + } + + fn deallocate_all(&mut self) { + self.deallocate_all_regs(); + } + + fn allocate_param_reg(&mut self, val_type: &LitTypeVariant) -> RegAllocResult { + match val_type { + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => self.alllocate_32bit_param_reg(), + LitTypeVariant::I64 => self.alllocate_64bit_param_reg(), + _ => Err(RegAllocError) + } + } + + fn deallocate_param_reg(&mut self, idx: RegIdx, val_type: &LitTypeVariant) { + match val_type { + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => self.deallocate_32bit_param_reg(idx), + LitTypeVariant::I64 => self.deallocate_64bit_param_reg(idx), + _ => () + } + } + + fn deallocate_all_param_regs(&mut self) { + self.deallocate_all_param_regs_(); + } + + fn name(&self, idx: RegIdx, val_type: &LitTypeVariant) -> String { + match val_type { + LitTypeVariant::U8 + | LitTypeVariant::I16 + | LitTypeVariant::I32 => { + if idx >= REG_32BIT_COUNT { + panic!("Not a valid 32-bit register"); + } + format!("w{}", idx) + }, + LitTypeVariant::I64 => { + if idx >= REG_64BIT_COUNT { + panic!("Not a valid 64-bit register"); + } + format!("x{}", idx) + }, + _ => panic!("Invalid data type for 'name'") + } + } +} + +impl Aarch64RegManager { + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + let mut regs32: HashMap = HashMap::new(); + let mut param_regs32: HashMap = HashMap::new(); + let mut regs64: HashMap = HashMap::new(); + let mut param_regs64: HashMap = HashMap::new(); + + for i in 0..=REG_64BIT_COUNT { + regs64.insert(format!("x{}", i), 0); + if i < 8 { + param_regs64.insert(format!("x{}", i), 0); + } + } + + for i in 0..=REG_32BIT_COUNT { + regs32.insert(format!("w{}", i), 0); + if i < 8 { + param_regs32.insert(format!("w{}", i), 0); + } + } + + Self { + regs32, + regs64, + param_regs32, + param_regs64 + } + } + + fn alllocate_32bit_reg(&mut self) -> RegAllocResult { + for i in 0..=REG_32BIT_COUNT { + let reg_name: String = format!("w{}", i); + if let Some(free_reg) = self.regs32.get(reg_name.clone().as_str()) { + if *free_reg == 1 { + self.regs32.insert(reg_name, 0); + return Ok(AllocedReg { + idx: i, + size: REG_32BIT + }); + } + } + } + Err(RegAllocError) + } + + fn alllocate_64bit_reg(&mut self) -> RegAllocResult { + for i in 0..=REG_64BIT_COUNT { + let reg_name: String = format!("x{}", i); + if let Some(free_reg) = self.regs64.get(reg_name.clone().as_str()) { + if *free_reg == 1 { + self.regs64.insert(reg_name, 0); + return Ok(AllocedReg { + idx: i, + size: REG_64BIT + }); + } + } + } + Err(RegAllocError) + } + + fn alllocate_32bit_param_reg(&mut self) -> RegAllocResult { + for i in 0..=REG_32BIT_COUNT { + let reg_name: String = format!("w{}", i); + if let Some(free_reg) = self.param_regs32.get(reg_name.clone().as_str()) { + if *free_reg == 1 { + self.regs32.insert(reg_name, 0); + return Ok(AllocedReg { + idx: i, + size: REG_32BIT + }); + } + } + } + Err(RegAllocError) + } + + fn alllocate_64bit_param_reg(&mut self) -> RegAllocResult { + for i in 0..=REG_64BIT_COUNT { + let reg_name: String = format!("x{}", i); + if let Some(free_reg) = self.param_regs64.get(reg_name.clone().as_str()) { + if *free_reg == 1 { + self.regs64.insert(reg_name, 0); + return Ok(AllocedReg { + idx: i, + size: REG_32BIT + }); + } + } + } + Err(RegAllocError) + } + + fn deallocate_32bit_reg(&mut self, index: RegIdx) { + let mut dealloc_name: String = String::from(""); + for (dindex, (reg_name, _)) in self.regs32.iter().enumerate() { + if dindex == index { + dealloc_name.push_str(reg_name); + break; + } + } + self.regs32.insert(dealloc_name, 1); + } + + fn deallocate_64bit_reg(&mut self, index: RegIdx) { + let mut dealloc_name: String = String::from(""); + for (dindex, (reg_name, _)) in self.regs64.iter().enumerate() { + if dindex == index { + dealloc_name.push_str(reg_name); + break; + } + } + self.regs64.insert(dealloc_name, 1); + } + + pub fn deallocate_32bit_param_reg(&mut self, index: usize) { + let mut dealloc_name: String = String::from(""); + for (dindex, (reg_name, _)) in self.param_regs32.iter().enumerate() { + if dindex == index { + dealloc_name.push_str(reg_name); + break; + } + } + self.param_regs32.insert(dealloc_name, 1); + } + + pub fn deallocate_64bit_param_reg(&mut self, index: usize) { + let mut dealloc_name: String = String::from(""); + for (dindex, (reg_name, _)) in self.param_regs64.iter().enumerate() { + if dindex == index { + dealloc_name.push_str(reg_name); + break; + } + } + self.param_regs64.insert(dealloc_name, 1); + } + + fn deallocate_all_regs(&mut self) { + for (_, reg_status) in self.regs32.iter_mut() { + *reg_status = 1; + } + for (_, reg_status) in self.regs64.iter_mut() { + *reg_status = 1; + } + } + + fn deallocate_all_param_regs_(&mut self) { + for (_, reg_status) in self.param_regs32.iter_mut() { + *reg_status = 1; + } + for (_, reg_status) in self.param_regs64.iter_mut() { + *reg_status = 1; + } + } +} diff --git a/src/code_gen/aarch64/mod.rs b/src/code_gen/aarch64/mod.rs index 2bdb97b..8d68d11 100644 --- a/src/code_gen/aarch64/mod.rs +++ b/src/code_gen/aarch64/mod.rs @@ -1,3 +1,5 @@ mod aarch64_codegen; +mod aarch64_reg; -pub use aarch64_codegen::Aarch64CodeGen; \ No newline at end of file +pub use aarch64_codegen::Aarch64CodeGen; +pub use aarch64_reg::*; \ No newline at end of file diff --git a/src/code_gen/cg_error.rs b/src/code_gen/cg_error.rs index a55d16f..0f828e9 100644 --- a/src/code_gen/cg_error.rs +++ b/src/code_gen/cg_error.rs @@ -1,4 +1,17 @@ pub enum CodeGenErr { NoContext, - UndefinedSymbol(String) + UndefinedSymbol +} + +impl CodeGenErr { + pub fn dump(&self) { + match self { + Self::NoContext => { + panic!("internal error: no context was provided for code generator") + }, + Self::UndefinedSymbol => { + panic!("internal error: code generator tried to search for a non-existing symbol") + } + } + } } \ No newline at end of file diff --git a/src/code_gen/codegen.rs b/src/code_gen/codegen.rs index b009e89..b49542b 100644 --- a/src/code_gen/codegen.rs +++ b/src/code_gen/codegen.rs @@ -40,10 +40,11 @@ use crate::types::LitType; use crate::types::LitTypeVariant; use crate::StorageClass; -use super::register::RegManager; +use super::reg::AllocedReg; +use super::reg::RegManager; use super::CodeGenErr; -pub type CodeGenResult = Result; +pub type CodeGenResult = Result; /// Indicating no register was produced from an code generation operation. pub const NO_REG: usize = 0xFFFFFFFF; @@ -69,14 +70,14 @@ pub trait CodeGen { /// let ast = AST::new(); /// generator.start_gen(&ast); /// ``` - fn start_gen(&mut self, nodes: Vec) where Self: Sized { + fn start_gen(&mut self, nodes: &Vec) where Self: Sized { self.gen_global_symbols(); // .text section starts from here println!("\n.text"); - for node in &nodes { + for node in nodes { let result: CodeGenResult = self.gen_code_from_ast(node, 0xFFFFFFFF, ASTOperation::AST_NONE); if result.is_err() { - println!("Error during code generation!"); + result.err().unwrap().dump(); } } } @@ -141,7 +142,7 @@ pub trait CodeGen { _ = self.gen_code_from_ast(right, reg, parent_ast_kind); self.reg_manager().deallocate_all(); } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else if ast_node.operation == ASTOperation::AST_FUNC_CALL { if ast_node.result_type == LitTypeVariant::Void { @@ -150,11 +151,11 @@ pub trait CodeGen { Stmt::FuncCall(func_call) => { return self.gen_func_call_stmt(func_call); }, - _ => return Ok(NO_REG) + _ => return Ok(AllocedReg::no_reg()) } } } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else if ast_node.operation == ASTOperation::AST_RETURN { let early_return = parent_ast_kind != ASTOperation::AST_FUNCTION; @@ -167,7 +168,8 @@ pub trait CodeGen { } self.gen_return_stmt(early_return) } - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) + }; } else if ast_node.operation == ASTOperation::AST_VAR_DECL { @@ -175,39 +177,39 @@ pub trait CodeGen { return match var_decl { Stmt::VarDecl(var_decl_stmt) => { if var_decl_stmt.class != StorageClass::LOCAL { - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else { let assign_expr: &ASTKind = &ast_node.left.as_ref().unwrap().kind; if let ASTKind::ExprAST(__expr) = assign_expr { _ = self.gen_local_var_decl_stmt(var_decl_stmt, __expr); } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } }, - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) }; } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else if ast_node.operation == ASTOperation::AST_ARR_VAR_DECL { if let ASTKind::StmtAST(arr_var_decl) = &ast_node.kind { return match arr_var_decl { Stmt::ArrVarDecl(arr_var_decl_stmt) => { if arr_var_decl_stmt.class != StorageClass::LOCAL { - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else { self.gen_local_arr_var_decl_stmt(arr_var_decl_stmt) } }, - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) } } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) } else if (ast_node.operation == ASTOperation::AST_NONE) || (ast_node.operation == ASTOperation::AST_ARR_VAR_DECL) { - return Ok(NO_REG); + return Ok(AllocedReg::no_reg()); } else if ast_node.operation == ASTOperation::AST_ASSIGN { let possible_assign_stmt: Stmt = ast_node.kind.clone().unwrap_stmt(); @@ -217,9 +219,9 @@ pub trait CodeGen { if let ASTKind::ExprAST(__expr) = assign_expr { _ = self.gen_var_assignment_stmt(&assign, __expr); } - Ok(NO_REG) + Ok(AllocedReg::no_reg()) }, - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) }; } else if ast_node.operation == ASTOperation::AST_LOOP { @@ -228,7 +230,7 @@ pub trait CodeGen { Stmt::Loop => { self.gen_loop_stmt(ast_node) }, - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) } } else { @@ -254,14 +256,11 @@ pub trait CodeGen { crate::ast::ASTKind::ExprAST(wexpr) => { self.gen_expr(&wexpr, curr_ast_kind, reg, parent_ast_kind) }, - crate::ast::ASTKind::Empty => Ok(NO_REG) + crate::ast::ASTKind::Empty => Ok(AllocedReg::no_reg()) } }, - Expr::Subscript(subs) => { - let index_reg: usize = self.gen_expr(&subs.index, curr_ast_kind, reg, parent_ast_kind)?; - self.gen_array_access2(subs.symtbl_pos, index_reg) - }, Expr::FuncCall(func_call) => self.gen_func_call_expr(func_call), + _ => Ok(AllocedReg::no_reg()) } } @@ -284,13 +283,13 @@ pub trait CodeGen { LitTypeVariant::I64 | LitTypeVariant::I32 => self.gen_load_intlit_into_reg(&lit_expr.value), LitTypeVariant::Str => self.gen_load_global_strlit(&lit_expr.value), - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) } } fn gen_bin_expr(&mut self, bin_expr: &BinExpr, curr_ast_kind: ASTOperation, reg: usize, parent_ast_kind: ASTOperation) -> CodeGenResult { - let leftreg: usize = self.gen_expr(&bin_expr.left, curr_ast_kind, reg, parent_ast_kind)?; - let rightreg: usize = self.gen_expr(&bin_expr.right, curr_ast_kind, reg, parent_ast_kind)?; + let leftreg = self.gen_expr(&bin_expr.left, curr_ast_kind, reg, parent_ast_kind)?; + let rightreg = self.gen_expr(&bin_expr.right, curr_ast_kind, reg, parent_ast_kind)?; match bin_expr.operation { ASTOperation::AST_ADD => self.gen_add(leftreg, rightreg), @@ -310,7 +309,7 @@ pub trait CodeGen { self.gen_cmp_and_set(bin_expr.operation, leftreg, rightreg) } }, - _ => Ok(NO_REG) + _ => Ok(AllocedReg::no_reg()) } } @@ -338,9 +337,9 @@ pub trait CodeGen { fn gen_label(&mut self, label: usize) -> CodeGenResult; - fn gen_cmp_and_jmp(&self, operation: ASTOperation, r1: usize, r2: usize, label: usize) -> CodeGenResult; + fn gen_cmp_and_jmp(&self, operation: ASTOperation, r1: AllocedReg, r2: AllocedReg, label: usize) -> CodeGenResult; - fn gen_cmp_and_set(&self, operation: ASTOperation, r1: usize, r2: usize) -> CodeGenResult; + fn gen_cmp_and_set(&self, operation: ASTOperation, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult; fn gen_function_stmt(&mut self, ast: &AST) -> CodeGenResult; @@ -357,21 +356,19 @@ pub trait CodeGen { /// The register index where the value of the symbol is loaded. fn gen_load_id_into_reg(&mut self, id: usize) -> CodeGenResult; - fn gen_store_reg_value_into_id(&mut self, reg: usize, id: usize) -> CodeGenResult; + fn gen_store_reg_value_into_id(&mut self, reg: AllocedReg, id: usize) -> CodeGenResult; - fn gen_add(&mut self, r1: usize, r2: usize) -> CodeGenResult; + fn gen_add(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult; - fn gen_sub(&mut self, r1: usize, r2: usize) -> CodeGenResult; + fn gen_sub(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult; - fn gen_mul(&mut self, r1: usize, r2: usize) -> CodeGenResult; + fn gen_mul(&mut self, r1: AllocedReg, r2: AllocedReg) -> CodeGenResult; fn gen_load_intlit_into_reg(&mut self, value: &LitType) -> CodeGenResult; fn gen_load_global_strlit(&mut self, symbol_id: &LitType) -> CodeGenResult; fn gen_array_access(&mut self, symbol_id: usize, expr: &AST) -> CodeGenResult; - - fn gen_array_access2(&mut self, symbol_id: usize, index: usize) -> CodeGenResult; fn gen_return_stmt(&mut self, early_return: bool) -> CodeGenResult; @@ -389,5 +386,5 @@ pub trait CodeGen { fn gen_break_stmt(&mut self, break_label: usize) -> CodeGenResult; - fn reg_manager(&self) -> RefMut; + fn reg_manager(&self) -> RefMut; } \ No newline at end of file diff --git a/src/code_gen/mod.rs b/src/code_gen/mod.rs index dabe941..9ea8f6e 100644 --- a/src/code_gen/mod.rs +++ b/src/code_gen/mod.rs @@ -1,10 +1,10 @@ mod aarch64; mod codegen; -mod register; mod common; mod cg_error; +mod reg; pub use aarch64::Aarch64CodeGen; +pub use aarch64::Aarch64RegManager; pub use codegen::CodeGen; -pub use register::RegManager; pub use cg_error::*; \ No newline at end of file diff --git a/src/code_gen/reg/mod.rs b/src/code_gen/reg/mod.rs new file mode 100644 index 0000000..8d4b80d --- /dev/null +++ b/src/code_gen/reg/mod.rs @@ -0,0 +1,5 @@ +mod reg_error; +mod register; + +pub use reg_error::*; +pub use register::*; \ No newline at end of file diff --git a/src/code_gen/reg/reg_error.rs b/src/code_gen/reg/reg_error.rs new file mode 100644 index 0000000..7363692 --- /dev/null +++ b/src/code_gen/reg/reg_error.rs @@ -0,0 +1 @@ +pub struct RegAllocError; \ No newline at end of file diff --git a/src/code_gen/reg/register.rs b/src/code_gen/reg/register.rs new file mode 100644 index 0000000..cb04cb4 --- /dev/null +++ b/src/code_gen/reg/register.rs @@ -0,0 +1,75 @@ +/* +MIT License + +Copyright (c) 2023 Kagati Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use crate::types::LitTypeVariant; + +use super::reg_error::RegAllocError; + +/// Register size +pub type RegSize = usize; + +/// Register index +pub type RegIdx = usize; + +pub const INVALID_REG_IDX: usize = 0xFFFFFFFF; + +pub struct AllocedReg { + pub size: RegSize, + pub idx: RegIdx +} + +impl AllocedReg { + pub fn no_reg() -> Self { + Self { + size: 0, + idx: INVALID_REG_IDX + } + } + + pub fn is_valid(&self) -> bool { + self.idx != INVALID_REG_IDX + } +} + +pub type RegAllocResult = Result; + +pub trait RegManager { + fn allocate(&mut self, var_type: &LitTypeVariant) -> RegAllocResult; + fn deallocate(&mut self, index: RegIdx, var_type: &LitTypeVariant); + fn deallocate_all(&mut self); + + fn allocate_param_reg(&mut self, var_type: &LitTypeVariant) -> RegAllocResult; + fn deallocate_param_reg(&mut self, idx: RegIdx, var_type: &LitTypeVariant); + fn deallocate_all_param_regs(&mut self); + + fn name(&self, idx: RegIdx, var_type: &LitTypeVariant) -> String; +} + +#[cfg(test)] +mod tests { + #[test] + fn test_allocation_of_one_register() { + + } +} \ No newline at end of file diff --git a/src/code_gen/register.rs b/src/code_gen/register.rs deleted file mode 100644 index ba8ed33..0000000 --- a/src/code_gen/register.rs +++ /dev/null @@ -1,165 +0,0 @@ -/* -MIT License - -Copyright (c) 2023 Kagati Foundation - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -use std::collections::HashMap; - -/// Manages the allocation and status of registers. -/// -/// This struct maintains a mapping between register names and their statuses, -/// where a status of '1' indicates that the register is free for allocation, -/// and a status of '0' indicates that the register is not available. -/// -/// # Fields -/// -/// * `registers` - A HashMap that maps register names (String) to their statuses (u8). -pub struct RegManager { - registers: HashMap, - param_regs: HashMap -} - -impl RegManager { - #[allow(clippy::new_without_default)] - pub fn new(reg_names: Vec, param_regs: Vec) -> Self { - Self { - registers: { - let mut regs: HashMap = HashMap::::new(); - for name in reg_names { - regs.insert(name, 1); - } - regs - }, - param_regs: { - let mut regs: HashMap = HashMap::::new(); - for name in param_regs { - regs.insert(name, 1); - } - regs - } - } - } - - pub fn allocate(&mut self) -> usize { - for i in 0..29 { - let reg_name = format!("x{}", i); - if let Some(free_reg) = self.registers.get(reg_name.clone().as_str()) { - if *free_reg == 1 { - self.registers.insert(reg_name, 0); - return i; - } - } - } - panic!("out of registers"); - } - - pub fn deallocate(&mut self, index: usize) { - let mut dealloc_name: String = String::from(""); - for (dindex, (reg_name, _)) in self.registers.iter().enumerate() { - if dindex == index { - dealloc_name.push_str(reg_name); - break; - } - } - self.registers.insert(dealloc_name, 1); - } - - pub fn allocate_param_reg(&mut self) -> usize { - for i in 0..8 { - let reg_name = format!("x{}", i); - if let Some(free_reg) = self.param_regs.get(reg_name.clone().as_str()) { - if *free_reg == 1 { - self.param_regs.insert(reg_name, 0); - return i; - } - } - } - panic!("out of parameter registers"); - } - - pub fn deallocate_param_reg(&mut self, index: usize) { - let mut dealloc_name: String = String::from(""); - for (dindex, (reg_name, _)) in self.param_regs.iter().enumerate() { - if dindex == index { - dealloc_name.push_str(reg_name); - break; - } - } - self.param_regs.insert(dealloc_name, 1); - } - - pub fn deallocate_all(&mut self) { - for (_, reg_status) in self.registers.iter_mut() { - *reg_status = 1; - } - self.deallocate_all_param_regs(); - } - - pub fn deallocate_all_param_regs(&mut self) { - for (_, reg_status) in self.param_regs.iter_mut() { - *reg_status = 1; - } - } - - pub fn name(&self, index: usize, offset: usize) -> String { - if index > 56 { - return String::from(""); - } - let mut reg_name_prefix = "x"; - if offset != 0 { - reg_name_prefix = "w"; - } - format!("{}{}", reg_name_prefix, index) - } -} - -#[cfg(test)] -mod tests { - use std::cell::RefCell; - use super::RegManager; - - #[test] - fn test_allocation_of_one_register() { - let rm: RefCell = RefCell::new( - RegManager::new( - { - let mut regs: Vec = vec![]; - for i in 0..28 { - regs.push(format!("x{}", i)); - regs.push(format!("w{}", i)); - } - regs - }, - { - let mut regs: Vec = vec![]; - for i in 0..7 { - regs.push(format!("x{}", i)); - regs.push(format!("w{}", i)); - } - regs - } - ) - ); - let reg: usize = rm.borrow_mut().allocate(); - assert!(reg != 0xFFFFFFFF); - } -} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 9f935d3..3d4dad7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -use crate::tokenizer::{Token, TokenPos}; +use crate::tokenizer::{Token, TokenKind, TokenPos}; /// Trait representing a basic error in the compiler. Any struct /// implementing this trait should provide a mechanism to report @@ -60,7 +60,10 @@ pub enum BTypeErr { #[derive(PartialEq, Clone, Debug)] pub enum BErrType { UndefinedSymbol, - UnexpectedToken, + UnexpectedToken { + expected: Vec, + found: Token + }, NonSubscriptable, NonCallable, SymbolAlreadyDefined, @@ -85,7 +88,6 @@ impl BErr { } let message: String = match err_type { BErrType::UndefinedSymbol => "Undefined symbol".to_string(), - BErrType::UnexpectedToken => "Unexpected token".to_string(), BErrType::NonSubscriptable => "Identifier is not subscriptable".to_string(), BErrType::NonCallable => "Identifier is not callable".to_string(), BErrType::SymbolAlreadyDefined => "Symbol already defined".to_string(), @@ -120,16 +122,36 @@ impl BErr { BErr::new(BErrType::UndefinedSymbol, source_file, err_token) } - pub fn unexpected_token(source_file: String, err_token: Token) -> BErr { - BErr::new(BErrType::UnexpectedToken, source_file, err_token) + pub fn unexpected_token(source_file: String, expected: Vec, found: Token) -> BErr { + let mut expect_token_msg: String = String::from(""); + if expected.len() > 1 { + expect_token_msg.push_str(" one of "); + } + for expect in &expected { + expect_token_msg.push_str(&format!("'{}'", expect.as_str())); + } + + BErr { + err_type: BErrType::UnexpectedToken { + expected: expected.clone(), + found: found.clone() + }, + info: Some( + BErrorInfo { + token: found.clone(), + message: format!("Unexpected token: expected {} but found '{}'", expect_token_msg, found.kind.as_str()), + source_file + } + ) + } } pub fn nonsubsriptable_ident(source_file: String, err_token: Token) -> BErr { - BErr::new(BErrType::UnexpectedToken, source_file, err_token) + BErr::new(BErrType::NonSubscriptable, source_file, err_token) } pub fn noncallable_ident(source_file: String, err_token: Token) -> BErr { - BErr::new(BErrType::UnexpectedToken, source_file, err_token) + BErr::new(BErrType::NonCallable, source_file, err_token) } pub fn symbol_already_defined(source_file: String, err_token: Token) -> BErr { @@ -143,7 +165,7 @@ impl BErr { pub fn report(&self) { if let Some(info) = &self.info { println!( - "{}:{}:{}: error: {}", + "{}:{}:{}: compile error: {}", info.source_file, info.token.pos.line, info.token.pos.column, diff --git a/src/main.rs b/src/main.rs index 053c567..768654d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,67 +31,69 @@ pub mod symbol; pub mod tokenizer; pub mod types; pub mod utils; +pub mod semantic; use std::{cell::RefCell, rc::Rc}; use ast::SourceFile; -use code_gen::{Aarch64CodeGen, RegManager}; +use code_gen::{Aarch64CodeGen, Aarch64RegManager}; use context::CompilerCtx; use parser::*; +use semantic::analyzer::SemanticAnalyzer; use symbol::*; use tokenizer::Tokenizer; fn main() { + // tokenizer let tokener: Rc> = Rc::new(RefCell::new(Tokenizer::new())); + + // parser let parsr: Rc> = Rc::new(RefCell::new(Parser::new(false))); + + // main symbol table let mut symt: Symtable = Symtable::new(); + + // main function table let mut funct: FunctionInfoTable = FunctionInfoTable::new(); - let mut file1: SourceFile = - SourceFile::new("/Users/rigelstar/Desktop/KagatiFoundation/bichara/examples/main.bic"); + + // example source file + let mut file1: SourceFile = SourceFile::new("/Users/rigelstar/Desktop/KagatiFoundation/bichara/examples/main.bic"); let mut source_files: Vec<&mut SourceFile> = vec![&mut file1]; + + // compiler context let ctx: Rc> = Rc::new(RefCell::new(CompilerCtx::new(&mut symt, &mut funct))); - let rm: RefCell = RefCell::new( - RegManager::new( - { - let mut regs: Vec = vec![]; - for i in 0..=28 { - regs.push(format!("x{}", i)); - } - for i in 29..=56 { - regs.push(format!("w{}", 56 - i)); - } - regs - }, - { - let mut regs: Vec = vec![]; - for i in 0..=7 { - regs.push(format!("x{}", i)); - } - for i in 8..=14 { - regs.push(format!("w{}", 14 - i)); - } - regs - } - ) - ); + + // semantic analyzer + let s_analyzer: SemanticAnalyzer = SemanticAnalyzer::new(Rc::clone(&ctx)); + + // register manager + let rm: RefCell = RefCell::new(Aarch64RegManager::new()); + + // aarch64 code generator let mut cg: Aarch64CodeGen = Aarch64CodeGen::new(rm); + for sf in &mut source_files { let read_res: Result = sf.read(); if let Err(e) = read_res { panic!("Error reading a source file: {:?}", e); } } + for sf in &mut source_files { sf.tokenize(Rc::clone(&tokener)); } + { let mut parser_borrow = parsr.borrow_mut(); for sf in &mut source_files { let tokens: Vec = sf.tokens.clone().unwrap(); ctx.borrow_mut().current_file = Some(sf); let parse_result: Vec = parser_borrow.parse_with_ctx(Rc::clone(&ctx), tokens); + + s_analyzer.start_analysis(&parse_result); + if !parser_borrow.has_parsing_errors() { - cg.gen_with_ctx(Rc::clone(&ctx), parse_result); + cg.gen_with_ctx(Rc::clone(&ctx), &parse_result); } } std::mem::drop(parser_borrow); diff --git a/src/parser/parse_error.rs b/src/parser/parse_error.rs index 5b198b9..fcaf312 100644 --- a/src/parser/parse_error.rs +++ b/src/parser/parse_error.rs @@ -26,7 +26,10 @@ use crate::{error, tokenizer::Token}; #[derive(Eq, PartialEq, Debug, Clone)] pub enum ParseError { - UnexpectedToken(Token), // unexpected token was encountered + UnexpectedToken { + expected: Token, + found: Token + }, // unexpected token was encountered SymbolNotFound(Token), // symbol not found; symbol has not been defined before NotCallable(Token), // if a token being called is not callable type GlobalInsideFunction(Token), diff --git a/src/parser/parser_impl.rs b/src/parser/parser_impl.rs index 7063001..61e3956 100644 --- a/src/parser/parser_impl.rs +++ b/src/parser/parser_impl.rs @@ -42,7 +42,6 @@ use crate::context::CompilerCtx; use crate::error::*; use crate::symbol::*; use crate::tokenizer::*; -use crate::types; use crate::types::*; use core::panic; use std::cell::RefCell; @@ -62,14 +61,7 @@ type TokenMatch<'a> = Result<&'a Token, Box>; /// to detect error states and invalid contexts. const INVALID_FUNC_ID: usize = 0xFFFFFFFF; -enum ParserScope { - LOCAL, - GLOBAL, -} - struct ParserContext { - function_id: usize, - scope: ParserScope, is_erronous_parse: bool, } @@ -136,11 +128,9 @@ impl<'parser> Parser<'parser> { temp_local_params: Symtable::new(), local_offset: 0, next_global_sym_pos: 0, - next_local_sym_pos: NSYMBOLS - 1, + next_local_sym_pos: 0, ctx: None, __pctx: ParserContext { - function_id: INVALID_FUNC_ID, - scope: ParserScope::GLOBAL, is_erronous_parse: false, }, __panic_mode: panic_mode @@ -223,6 +213,7 @@ impl<'parser> Parser<'parser> { _ => { let __err: Result<_, Box> = Err(Box::new(BErr::unexpected_token( self.get_current_file_name(), + vec![TokenKind::KW_LET, TokenKind::KW_IF, TokenKind::KW_WHILE, TokenKind::KW_FOR, TokenKind::KW_LOOP], self.current_token.clone(), ))); if self.__panic_mode { @@ -265,18 +256,20 @@ impl<'parser> Parser<'parser> { stmt_count += 1; } if stmt_count == 0 { - Ok(AST::empty()) - } else if let Some(node) = left { + return Ok(AST::empty()); + } + if let Some(node) = left { Ok(node) } else { let __err: Result> = Err(Box::new(BErr::unexpected_token( self.get_current_file_name(), + vec![], self.current_token.clone(), ))); if self.__panic_mode { panic!("{:?}", __err); } - return __err; + __err } } @@ -326,6 +319,7 @@ impl<'parser> Parser<'parser> { if !is_tok_comma && !is_tok_rparen { let __err: Result<_, Box> = Err(Box::new(BErr::unexpected_token( current_file.clone(), + vec![TokenKind::T_COMMA, TokenKind::T_RPAREN], self.current_token.clone() ))); if self.__panic_mode { @@ -400,7 +394,8 @@ impl<'parser> Parser<'parser> { stack_offset, func_return_type, func_storage_class, - func_params + func_params, + self.temp_local_syms.clone() ); // create a new FunctionInfo if let Some(ctx_rc) = &mut self.ctx { @@ -443,14 +438,14 @@ impl<'parser> Parser<'parser> { return __err; } _ = self.token_match(TokenKind::T_ARROW)?; - let func_ret_type: LitTypeVariant = self.parse_id_type(); + let func_ret_type: LitTypeVariant = self.parse_id_type()?; Ok(func_ret_type) } fn parse_parameter(&mut self) -> Result> { let param_name: Token = self.token_match(TokenKind::T_IDENTIFIER)?.clone(); let _ = self.token_match(TokenKind::T_COLON)?; - let param_type: LitTypeVariant = self.parse_id_type(); + let param_type: LitTypeVariant = self.parse_id_type()?; let param_loc_off: i32 = self.gen_next_local_offset(param_type); self.skip_to_next_token(); Ok(FuncParam { @@ -478,6 +473,7 @@ impl<'parser> Parser<'parser> { if self.current_function_id == INVALID_FUNC_ID { let __err: Result<_, Box> = Err(Box::new(BErr::unexpected_token( self.get_current_file_name(), + vec![], self.current_token.clone() ))); if self.__panic_mode { @@ -512,6 +508,7 @@ impl<'parser> Parser<'parser> { if self.current_token.kind != TokenKind::T_SEMICOLON { let __err: Result<_, Box> = Err(Box::new(BErr::unexpected_token( self.get_current_file_name(), + vec![TokenKind::T_SEMICOLON], self.current_token.clone() ))); if self.__panic_mode { @@ -527,6 +524,8 @@ impl<'parser> Parser<'parser> { })), ASTOperation::AST_RETURN, LitTypeVariant::Void, + None, + None )); } let return_expr: AST = self.parse_equality()?; @@ -716,7 +715,7 @@ impl<'parser> Parser<'parser> { return self.parse_array_var_decl_stmt(&id_token); } - var_type = self.parse_id_type(); + var_type = self.parse_id_type()?; self.skip_to_next_token(); } @@ -736,9 +735,10 @@ impl<'parser> Parser<'parser> { let mut default_value: Option = None; // if there is some error during expression parsing - if let Some(Err(parse_error)) = assignment_parse_res { - return Err(parse_error); - } else if let Some(Ok(ref res)) = assignment_parse_res { + if let Some(Err(parse_err)) = assignment_parse_res { + return Err(parse_err); + } + else if let Some(Ok(ref res)) = assignment_parse_res { // if the variable being declared is a global variable and // has some value assigned to it, then that assigned value // can be evaluated at compile time as global expressions @@ -750,26 +750,9 @@ impl<'parser> Parser<'parser> { } } } - // else get the type that results from evaluating the expression - let assign_value_type: LitTypeVariant = self.determine_type(assignment_parse_res.as_ref().unwrap()); - - if (var_type != LitTypeVariant::None) - && (var_type != assign_value_type) - && (!is_type_coalescing_possible(assign_value_type, var_type)) { - return Err(Box::new(BErr::new( - BErrType::TypeError(BTypeErr::AssignmentTypeMismatch { - var_type: var_type.to_string(), - assigned_type: assign_value_type.to_string(), - }), - self.get_current_file_name(), - self.current_token.clone(), - ))); - } else { - if var_type == LitTypeVariant::Str { - let str_const_label: usize = self.ctx.as_ref().unwrap().borrow_mut().label_id - 1; - default_value = Some(LitType::I32(str_const_label as i32)); - } - var_type = assign_value_type; + else if var_type == LitTypeVariant::Str { + let str_const_label: usize = self.ctx.as_ref().unwrap().borrow_mut().label_id - 1; + default_value = Some(LitType::I32(str_const_label as i32)); } } // TODO:: CHECK IF THE VARIABLE EXISTS IN THE SAME SCOPE ALREADY!!! @@ -818,17 +801,6 @@ impl<'parser> Parser<'parser> { return_result } - fn determine_type(&self, res: &ParseResult2) -> LitTypeVariant { - if let Ok(ast) = res { - return match ast.kind.clone() { - ASTKind::Empty => LitTypeVariant::None, - ASTKind::ExprAST(ref expr) => infer_type_from_expr(expr), - ASTKind::StmtAST(_) => panic!("Can't determine type of this ParseResult"), - }; - } - panic!("Cannot determine the type of this ParseResult."); - } - fn gen_next_local_offset(&mut self, var_type: LitTypeVariant) -> i32 { let temp_offset: i32 = if var_type.size() > 4 { var_type.size() as i32 @@ -842,8 +814,14 @@ impl<'parser> Parser<'parser> { // TODO: Write comments fn parse_array_var_decl_stmt(&mut self, id_token: &Token) -> ParseResult2 { self.skip_to_next_token(); // skip '[' - // array type - let array_type: LitTypeVariant = self.parse_id_type(); + + // array type token + let array_type_token: Token = self.current_token.clone(); + + // extract type from the type token + let array_type: LitTypeVariant = self.parse_id_type()?; + + // skip the type token self.skip_to_next_token(); // semicolon before the array size @@ -863,9 +841,14 @@ impl<'parser> Parser<'parser> { } } if array_size_type == TokenKind::T_NONE { - panic!( - "Array size must be specified with an integer value. Given: '{:?}'", - array_size_token.lexeme + return Err( + Box::new( + BErr::unexpected_token( + self.get_current_file_name(), + vec![TokenKind::T_INT_NUM], + array_size_token + ) + ) ); } let array_size: usize = array_size_token.lexeme.parse::().unwrap(); @@ -884,7 +867,7 @@ impl<'parser> Parser<'parser> { None ); - self.local_offset += (array_size * array_type.size()) as i32; + self.local_offset += (array_size * array_type.size()) as i32 + 4; // allocate '4' extra bytes for size information let symbol_add_pos: usize = if self.is_scope_global() { self.add_symbol_global(sym.clone()).unwrap() @@ -892,52 +875,38 @@ impl<'parser> Parser<'parser> { self.add_symbol_local(sym.clone()).unwrap() }; - let array_values: Vec = self.parse_array_assign_values(sym.lit_type)?; + let array_values: Vec = self.parse_array_assign_values()?; - let final_result: Result> = Ok(AST::new( + let array_decl_ast: AST = AST::create_leaf( ASTKind::StmtAST(Stmt::ArrVarDecl(ArrVarDeclStmt { symtbl_pos: symbol_add_pos, vals: array_values, class: sym.class })), ASTOperation::AST_ARR_VAR_DECL, - None, - None, sym.lit_type, - )); + Some(array_type_token), + None + ); + _ = self.token_match(TokenKind::T_SEMICOLON)?; - final_result + Ok(array_decl_ast ) } // parsing array values - fn parse_array_assign_values(&mut self, lit_type: LitTypeVariant) -> Result, Box> { + fn parse_array_assign_values(&mut self) -> Result, Box> { _ = self.token_match(TokenKind::T_LBRACKET)?; let mut vals: Vec = vec![]; if self.current_token.kind != TokenKind::T_RBRACKET { loop { let argu: AST = self.parse_equality()?; - if argu.result_type != lit_type - && !is_type_coalescing_possible(argu.result_type, argu.result_type) - { - let _err: Box = Box::new(BErr::new( - BErrType::TypeError(BTypeErr::AssignmentTypeMismatch { - var_type: format!("{}[]", lit_type), - assigned_type: argu.result_type.to_string() - }), - self.get_current_file_name(), - self.current_token.clone() - )); - if self.__panic_mode { - panic!("{:?}", _err); - } - return Err(_err); - } vals.push(argu.kind.unwrap_expr()); let is_tok_comma: bool = self.current_token.kind == TokenKind::T_COMMA; let is_tok_rparen: bool = self.current_token.kind == TokenKind::T_RBRACKET; if !is_tok_comma && !is_tok_rparen { let __err: Box = Box::new(BErr::unexpected_token( self.get_current_file_name(), + vec![TokenKind::T_COMMA, TokenKind::T_RBRACKET], self.current_token.clone() )); if self.__panic_mode { @@ -956,15 +925,23 @@ impl<'parser> Parser<'parser> { Ok(vals) } - fn parse_id_type(&mut self) -> LitTypeVariant { + fn parse_id_type(&mut self) -> Result> { let current_tok: TokenKind = self.current_token.kind; match current_tok { - TokenKind::KW_INT => LitTypeVariant::I32, - TokenKind::KW_CHAR => LitTypeVariant::U8, - TokenKind::KW_STR => LitTypeVariant::Str, - TokenKind::KW_LONG => LitTypeVariant::I64, - TokenKind::KW_VOID => LitTypeVariant::Void, - _ => panic!("{:?} is not a valid datatype.", current_tok), + TokenKind::KW_INT => Ok(LitTypeVariant::I32), + TokenKind::KW_CHAR => Ok(LitTypeVariant::U8), + TokenKind::KW_STR => Ok(LitTypeVariant::Str), + TokenKind::KW_LONG => Ok(LitTypeVariant::I64), + TokenKind::KW_VOID => Ok(LitTypeVariant::Void), + _ => { + Err(Box::new( + BErr::unexpected_token( + self.get_current_file_name(), + vec![TokenKind::T_DTYPE], + self.current_token.clone() + ) + )) + } } } @@ -1001,6 +978,7 @@ impl<'parser> Parser<'parser> { id_token.clone(), ))); } + let id_index_symt: usize = symbol_search_result.unwrap().0; let symbol: Symbol = if let Some(ctx_rc) = &mut self.ctx { let ctx_borrow = ctx_rc.borrow_mut(); @@ -1016,6 +994,7 @@ impl<'parser> Parser<'parser> { self.current_token.clone(), ))); }; + // we are in global scope but trying to assign to a local variable if self.is_scope_global() && symbol.class == StorageClass::LOCAL { self.skip_past(TokenKind::T_SEMICOLON); @@ -1024,39 +1003,41 @@ impl<'parser> Parser<'parser> { id_token.clone(), ))); } + // Check if we are assigning to a type other than SymbolType::Variable. If yes, panic! if symbol.sym_type != SymbolType::Variable { // self.skip_past(TokenKind::T_SEMICOLON); panic!("Assigning to type '{:?}' is not allowed! '{:?}'", symbol.sym_type, symbol); } + _ = self.token_match(TokenKind::T_EQUAL)?; - let mut bin_expr_ast_node: AST = self.parse_equality()?; - let compat_node: Option = Parser::validate_assign_compatibility(&symbol, &mut bin_expr_ast_node); + + let bin_expr_ast_node: AST = self.parse_equality()?; + // following code is going to break at some point. unwrap()ing an Option type without checking? - let _result_type: LitTypeVariant = compat_node.as_ref().unwrap().result_type; + let _result_type: LitTypeVariant = bin_expr_ast_node.result_type; + _ = self.token_match(TokenKind::T_SEMICOLON)?; + let lvalueid: AST = AST::create_leaf( ASTKind::StmtAST(Stmt::LValue(id_index_symt)), ASTOperation::AST_LVIDENT, symbol.lit_type, + None, + None ); + Ok(AST::new( ASTKind::StmtAST(Stmt::Assignment(AssignStmt { symtbl_pos: id_index_symt, })), ASTOperation::AST_ASSIGN, Some(lvalueid), - compat_node, + Some(bin_expr_ast_node), _result_type, )) } - fn validate_assign_compatibility(symbol: &Symbol, node: &mut AST) -> Option { - let compat_node: Option = types::modify_ast_node_type(node, symbol.lit_type); - compat_node.as_ref()?; - compat_node - } - fn parse_equality(&mut self) -> ParseResult2 { let left: ParseResult2 = self.parse_comparision(); self.try_parsing_binary(left, vec![TokenKind::T_EQEQ, TokenKind::T_NEQ]) @@ -1098,20 +1079,6 @@ impl<'parser> Parser<'parser> { self.skip_to_next_token(); // skip the operator let ast_op: ASTOperation = ASTOperation::from_token_kind(current_token_kind); let right: AST = self.parse_equality()?; - let compat_res: (bool, LitTypeVariant) = - are_compatible_for_operation(&left, &right, ast_op); - if !compat_res.0 { - return Err(Box::new(BErr::new( - BErrType::TypeError(BTypeErr::IncompatibleTypes { - first_type: left.result_type.to_string(), - second_type: right.result_type.to_string(), - operator: format!("{:?}", ast_op), - }), - self.get_current_file_name(), - self.current_token.clone(), - ))); - } - let result_type: LitTypeVariant = compat_res.1; let left_expr: Expr = left.kind.unwrap_expr(); let right_expr: Expr = right.kind.unwrap_expr(); Ok(AST::create_leaf( @@ -1119,10 +1086,12 @@ impl<'parser> Parser<'parser> { operation: ast_op, left: Box::new(left_expr), right: Box::new(right_expr), - result_type, + result_type: LitTypeVariant::None, })), ast_op, - result_type, + LitTypeVariant::None, + None, + None )) } @@ -1131,7 +1100,6 @@ impl<'parser> Parser<'parser> { return ctx_rc .borrow_mut() .current_file - .as_ref() .unwrap() .name .clone(); @@ -1162,14 +1130,14 @@ impl<'parser> Parser<'parser> { ASTOperation::AST_INTLIT, )), TokenKind::T_STRING => { - let mut str_label: i32; - if let Some(ctx_rc) = &mut self.ctx { + let str_label: i32 = if let Some(ctx_rc) = &mut self.ctx { let mut ctx_borrow = ctx_rc.borrow_mut(); - str_label = ctx_borrow.label_id as i32; + let _lbl: i32 = ctx_borrow.label_id as i32; ctx_borrow.incr_label_count(); + _lbl } else { panic!("No context provided for parser!"); - } + }; let str_const_symbol: Symbol = Symbol::new( format!("_L{}---{}", str_label, current_token.lexeme.clone()), LitTypeVariant::Str, @@ -1178,20 +1146,34 @@ impl<'parser> Parser<'parser> { ); self.add_symbol_global(str_const_symbol); Ok(AST::create_leaf( - ASTKind::ExprAST(Expr::LitVal(LitValExpr { - value: LitType::Str(current_token.lexeme.clone(), str_label as usize), - result_type: LitTypeVariant::Str, - })), + ASTKind::ExprAST( + Expr::LitVal( + LitValExpr { + value: LitType::Str(current_token.lexeme.clone(), str_label as usize), + result_type: LitTypeVariant::Str, + } + ) + ), ASTOperation::AST_STRLIT, LitTypeVariant::Str, + None, + None )) } TokenKind::T_IDENTIFIER => { // Identifiers in a global variable declaration expression are not allowed. if self.is_scope_global() { - return Err(Box::new(BErr::new(BErrType::TypeError(BTypeErr::InitializerNotAConstant { - lexeme: current_token.lexeme.clone() - }), current_file.clone(), current_token.clone()))); + return Err(Box::new( + BErr::new( + BErrType::TypeError( + BTypeErr::InitializerNotAConstant { + lexeme: current_token.lexeme.clone() + } + ), + current_file.clone(), + current_token.clone() + ) + )); } let sym_find_res: Option<(usize, StorageClass)> = self.find_symbol(¤t_token.lexeme); if sym_find_res.is_none() { @@ -1227,6 +1209,8 @@ impl<'parser> Parser<'parser> { })), ASTOperation::AST_IDENT, symbol.lit_type, + None, + None )) } } @@ -1242,6 +1226,7 @@ impl<'parser> Parser<'parser> { Box::new( BErr::unexpected_token( current_file.clone(), + vec![TokenKind::T_EXPR], current_token.clone(), ) ) @@ -1262,6 +1247,8 @@ impl<'parser> Parser<'parser> { })), operation, value.variant(), + None, + None ) } @@ -1294,6 +1281,8 @@ impl<'parser> Parser<'parser> { })), ASTOperation::AST_ARRAY_ACCESS, indexed_symbol.lit_type, + None, + None )) } @@ -1323,6 +1312,7 @@ impl<'parser> Parser<'parser> { if !is_tok_comma && !is_tok_rparen { let __err: Result> = Err(Box::new(BErr::unexpected_token( current_file.clone(), + vec![TokenKind::T_COMMA, TokenKind::T_RPAREN], self.current_token.clone() ))); if self.__panic_mode { @@ -1354,7 +1344,9 @@ impl<'parser> Parser<'parser> { } )), ASTOperation::AST_FUNC_CALL, - LitTypeVariant::Void + LitTypeVariant::Void, + None, + None ) ) } @@ -1371,6 +1363,8 @@ impl<'parser> Parser<'parser> { ), ASTOperation::AST_FUNC_CALL, called_symbol.lit_type, + None, + None )) } } @@ -1389,16 +1383,8 @@ impl<'parser> Parser<'parser> { } fn add_symbol_local(&mut self, sym: Symbol) -> Option { - let insert_pos: Option = { - if let Some(ctx_rc) = &mut self.ctx { - let mut ctx_borrow = ctx_rc.borrow_mut(); - self.temp_local_syms.insert(self.next_local_sym_pos, sym.clone()); - ctx_borrow.sym_table.insert(self.next_local_sym_pos, sym) - } else { - panic!("Can't add a new symbol locally"); - } - }; - self.next_local_sym_pos -= 1; + let insert_pos: Option = self.temp_local_syms.insert(self.next_local_sym_pos, sym.clone()); + self.next_local_sym_pos += 1; insert_pos } @@ -1429,18 +1415,12 @@ impl<'parser> Parser<'parser> { } fn find_symbol_local(&self, param_name: &str) -> Option { - if let Some(ctx_rc) = &self.ctx { - let ctx_borrow = ctx_rc.borrow_mut(); - for index in (self.next_local_sym_pos + 1)..NSYMBOLS { - if let Some(symbol) = ctx_borrow.sym_table.get_symbol(index) { - let has_local_sym: Option = self.temp_local_syms.find_symbol(param_name); - if symbol.name == param_name && has_local_sym.is_some() { - return Some(index); - } - } - } + let has_local_sym: Option = self.temp_local_syms.find_symbol(param_name); + if has_local_sym.is_some() { + has_local_sym + } else { + None } - None } fn is_scope_global(&self) -> bool { @@ -1458,7 +1438,15 @@ impl<'parser> Parser<'parser> { fn token_match_no_advance(&mut self, kind: TokenKind) -> TokenMatch { let current: Token = self.current_token.clone(); if kind != current.kind { - return Err(Box::new(BErr::unexpected_token(self.get_current_file_name(), current))); + return Err( + Box::new( + BErr::unexpected_token( + self.get_current_file_name(), + vec![kind], + current + ) + ) + ); } // self.skip_to_next_token(); Ok(&self.tokens[self.current - 1]) @@ -1467,7 +1455,15 @@ impl<'parser> Parser<'parser> { fn token_match(&mut self, kind: TokenKind) -> TokenMatch { let current: Token = self.current_token.clone(); if kind != current.kind { - let __err: Result<_, Box> = Err(Box::new(BErr::unexpected_token(self.get_current_file_name(), current))); + let __err: Result<_, Box> = Err( + Box::new( + BErr::unexpected_token( + self.get_current_file_name(), + vec![kind], + current + ) + ) + ); if self.__panic_mode { panic!("{:?}", __err); } diff --git a/src/semantic/analyzer.rs b/src/semantic/analyzer.rs new file mode 100644 index 0000000..ac949cb --- /dev/null +++ b/src/semantic/analyzer.rs @@ -0,0 +1,314 @@ +/* +MIT License + +Copyright (c) 2023 Kagati Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use std::{cell::RefCell, rc::Rc}; + +use crate::{ast::{ASTKind, ASTOperation, FuncCallExpr, Stmt, AST}, context::CompilerCtx, FunctionInfo, Symbol}; + +use super::{sa_errors::SAError, sa_types::SAResult, type_checker::TypeChecker}; + +pub struct SemanticAnalyzer<'sa> { + pub ctx: Rc>> +} + +impl<'sa> SemanticAnalyzer<'sa> { + pub fn new(ctx: Rc>>) -> Self { + Self { + ctx + } + } + + pub fn start_analysis(&self, nodes: &Vec) { + for node in nodes { + let _result: SAResult = self.analyze_node(node, ASTOperation::AST_NONE); + } + } + + fn analyze_node(&self, node: &AST, parent_ast_op: ASTOperation) -> SAResult { + match node.operation { + ASTOperation::AST_VAR_DECL => self.analyze_var_decl_stmt(node), + ASTOperation::AST_FUNCTION => self.analyze_func_decl_stmt(node), + ASTOperation::AST_FUNC_CALL => self.analyze_func_call_expr(node), + ASTOperation::AST_GLUE => { + if let Some(left) = node.left.as_ref() { + _ = self.analyze_node(left, parent_ast_op); + } + if let Some(right) = node.right.as_ref() { + _ = self.analyze_node(right, parent_ast_op); + } + Ok(()) + } + _ => panic!("'{:?}' is not supported ASTOperation for 'analyze_node' yet!", node.operation) + } + } + + fn analyze_func_decl_stmt(&self, node: &AST) -> SAResult { + let func_id: usize = match &node.kind { + ASTKind::StmtAST(stmt_ast) => { + match stmt_ast { + Stmt::FuncDecl(func_decl_stmt) => { + func_decl_stmt.func_id + }, + _ => panic!("not a function declaration statement") + } + }, + _ => panic!("not a statement") + }; + + let ctx_borrow: std::cell::Ref> = self.ctx.borrow(); + let func_name: String = ctx_borrow.sym_table.get_symbol(func_id).unwrap().name.clone(); + if ctx_borrow.func_table.get(&func_name).is_none() { + panic!("Function '{}' not found", func_name); + }; + drop(ctx_borrow); + + if let Some(func_body) = &node.left { + return self.analyze_node(func_body, ASTOperation::AST_FUNCTION); + } + Ok(()) + } + + fn analyze_func_call_expr(&self, node: &AST) -> SAResult { + Ok(()) + } + + fn analyze_var_decl_stmt(&self, node: &AST) -> SAResult { + let mut ctx_borrow = self.ctx.borrow_mut(); + if let ASTKind::StmtAST(stmt) = &node.kind { + return match stmt { + Stmt::VarDecl(var_decl_stmt) => { + let symbol: &mut Symbol = ctx_borrow.sym_table.get_mut_or_fail(var_decl_stmt.symtbl_pos); + let assign_expr: &ASTKind = &node.left.as_ref().unwrap().kind; + + if let ASTKind::ExprAST(expr) = assign_expr { + TypeChecker::type_check_var_decl_stmt(symbol, expr) + } else { + Ok(()) + } + }, + Stmt::ArrVarDecl(arr_var_decl_stmt) => { + let sym: &mut Symbol = ctx_borrow.sym_table.get_mut_or_fail(arr_var_decl_stmt.symtbl_pos); + // array size validation + if sym.size != arr_var_decl_stmt.vals.len() { + return Err(SAError::ArrayLengthError { + expected: sym.size, + found: arr_var_decl_stmt.vals.len() + }); + } + TypeChecker::type_check_arr_var_decl_stmt(sym, &arr_var_decl_stmt.vals) + }, + _ => panic!("Not a variable declaration statement...") + }; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::{cell::RefCell, rc::Rc}; + + use crate::{ + ast::{ + ASTKind, ASTOperation, BinExpr, Expr, LitValExpr, Stmt, VarDeclStmt, AST + }, + context::CompilerCtx, + semantic::{sa_errors::{SAError, SATypeError}, sa_types::SAResult}, + types::{ + LitType, + LitTypeVariant + }, + FunctionInfoTable, + StorageClass, + Symbol, + SymbolType, + Symtable + }; + + use super::SemanticAnalyzer; + + fn create_symt(syms: Vec) -> Symtable { + let mut symt = Symtable::::new(); + for sym in syms { + symt.add_symbol(sym); + } + symt + } + + fn create_funt() -> FunctionInfoTable { + FunctionInfoTable::new() + } + + fn create_ctx<'ctx>(symt: &'ctx mut Symtable, func_table: &'ctx mut FunctionInfoTable) -> CompilerCtx<'ctx> { + CompilerCtx::new(symt, func_table) + } + + fn create_i32_var_decl_ast(pos: usize) -> AST { + AST::new( + ASTKind::StmtAST(Stmt::VarDecl( + VarDeclStmt { + class: StorageClass::LOCAL, + symtbl_pos: pos + } + )), + ASTOperation::AST_VAR_DECL, + Some(AST::create_leaf( + ASTKind::ExprAST( + Expr::LitVal(LitValExpr { + value: LitType::I32(123), + result_type: LitTypeVariant::I32 + }) + ), + ASTOperation::AST_INTLIT, + LitTypeVariant::I32, + None, + None + ) + ), + None, + LitTypeVariant::I32 + ) + } + + fn create_i32_var_decl_ast_with_bin_expr(pos: usize) -> AST { + AST::new( + ASTKind::StmtAST(Stmt::VarDecl( + VarDeclStmt { + class: StorageClass::LOCAL, + symtbl_pos: pos + } + )), + ASTOperation::AST_VAR_DECL, + Some(AST::create_leaf( + ASTKind::ExprAST( + Expr::Binary(BinExpr { + operation: ASTOperation::AST_ADD, + left: Box::new( + Expr::LitVal(LitValExpr { + value: LitType::I32(123), + result_type: LitTypeVariant::I32 + }) + ), + right: Box::new( + Expr::LitVal( + LitValExpr { + value: LitType::Str("bichara".to_string(), 7), + result_type: LitTypeVariant::Str + } + ) + ), + result_type: LitTypeVariant::I32 + }) + + ), + ASTOperation::AST_INTLIT, + LitTypeVariant::I32, + None, + None + ) + ), + None, + LitTypeVariant::I32 + ) + } + + fn create_i32_symbol(name: String) -> Symbol { + Symbol::new(name, LitTypeVariant::I32, SymbolType::Variable, StorageClass::LOCAL) + } + + fn create_symbol_without_explicit_type(name: String) -> Symbol { + Symbol::new(name, LitTypeVariant::None, SymbolType::Variable, StorageClass::LOCAL) + } + + #[test] + fn test_no_type_var_decl_stmt_analysis() { + let symt: &mut Symtable = &mut create_symt( + vec![ + create_symbol_without_explicit_type("number".to_string()), + ] + ); + let funct: &mut FunctionInfoTable = &mut create_funt(); + let ctx: Rc>> = Rc::new(RefCell::new(create_ctx(symt, funct))); + + let a_analyzer: SemanticAnalyzer<'_> = SemanticAnalyzer::new(Rc::clone(&ctx)); + + let no_type_var_ast: AST = create_i32_var_decl_ast(0); + let ar2: SAResult = a_analyzer.analyze_var_decl_stmt(&no_type_var_ast); + assert!(ar2.is_ok()); + + // type has to be updated of non-type symbol + assert_eq!(symt.get_symbol(0).unwrap().lit_type, LitTypeVariant::I32); + } + + #[test] + fn test_bin_expr_var_decl_stmt_analysis() { + let symt: &mut Symtable = &mut create_symt( + vec![ + create_symbol_without_explicit_type("number".to_string()), + ] + ); + let funct: &mut FunctionInfoTable = &mut create_funt(); + let ctx: Rc>> = Rc::new(RefCell::new(create_ctx(symt, funct))); + + let a_analyzer: SemanticAnalyzer<'_> = SemanticAnalyzer::new(Rc::clone(&ctx)); + + let no_type_var_ast: AST = create_i32_var_decl_ast_with_bin_expr(0); + let ar2: SAResult = a_analyzer.analyze_var_decl_stmt(&no_type_var_ast); + assert!(ar2.is_err()); + matches!( + ar2, + Err( + SAError::TypeError( + SATypeError::IncompatibleTypes { + a: LitTypeVariant::I32, + b: LitTypeVariant::Str, + operation: ASTOperation::AST_ADD + } + ) + ) + ); + + // type has to be updated of non-type symbol + // assert_eq!(symt.get_symbol(0).unwrap().lit_type, LitTypeVariant::I32); + } + + #[test] + fn test_var_decl_stmt_analysis() { + let symt: &mut Symtable = &mut create_symt( + vec![ + create_i32_symbol("number".to_string()), + create_i32_symbol("number2".to_string()) + ] + ); + let funct: &mut FunctionInfoTable = &mut create_funt(); + let ctx: Rc>> = Rc::new(RefCell::new(create_ctx(symt, funct))); + + let var_ast: AST = create_i32_var_decl_ast(0); + + let a_analyzer: SemanticAnalyzer<'_> = SemanticAnalyzer::new(Rc::clone(&ctx)); + + let analysis_res: SAResult = a_analyzer.analyze_var_decl_stmt(&var_ast); + assert!(analysis_res.is_ok()); + } +} \ No newline at end of file diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs new file mode 100644 index 0000000..edd059c --- /dev/null +++ b/src/semantic/mod.rs @@ -0,0 +1,28 @@ +/* +MIT License + +Copyright (c) 2023 Kagati Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +pub mod analyzer; +pub mod type_checker; +pub mod sa_errors; +pub mod sa_types; \ No newline at end of file diff --git a/src/semantic/sa_errors.rs b/src/semantic/sa_errors.rs new file mode 100644 index 0000000..190c0a4 --- /dev/null +++ b/src/semantic/sa_errors.rs @@ -0,0 +1,45 @@ +/* +MIT License + +Copyright (c) 2023 Kagati Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use crate::{ast::ASTOperation, types::LitTypeVariant}; + +pub enum SATypeError { + AssignmentTypeMismatch { + expected: LitTypeVariant, found: LitTypeVariant + }, + IncompatibleTypes { + a: LitTypeVariant, + b: LitTypeVariant, + operation: ASTOperation + }, + TypeMismatch { + a: LitTypeVariant, b: LitTypeVariant + } +} + +pub enum SAError { + TypeError(SATypeError), + ArrayLengthError { expected: usize, found: usize }, + None +} \ No newline at end of file diff --git a/src/semantic/sa_types.rs b/src/semantic/sa_types.rs new file mode 100644 index 0000000..2d8e504 --- /dev/null +++ b/src/semantic/sa_types.rs @@ -0,0 +1,3 @@ +use super::sa_errors::SAError; + +pub type SAResult = Result<(), SAError>; \ No newline at end of file diff --git a/src/semantic/type_checker.rs b/src/semantic/type_checker.rs new file mode 100644 index 0000000..7a14892 --- /dev/null +++ b/src/semantic/type_checker.rs @@ -0,0 +1,98 @@ +/* +MIT License + +Copyright (c) 2023 Kagati Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +use crate::{ + ast::Expr, + types::{ + is_type_coalescing_possible, LitTypeVariant + }, + Symbol +}; + +use super::{ + sa_errors::{ + SAError, + SATypeError + }, + sa_types::SAResult +}; + +pub struct TypeChecker; + +impl TypeChecker { + /// Performs type checking for variable declaration statements. + /// + /// This function infers the type of an expression assigned to a variable, + /// updates the variable's symbol if it was declared without an explicit + /// type, and ensures the assigned expression's type matches the declared or + /// inferred type. If there's a type mismatch that cannot be reconciled, an + /// error is returned. + pub fn type_check_var_decl_stmt(var_decl_sym: &mut Symbol, expr: &Expr) -> SAResult { + let expr_res_type: LitTypeVariant = Self::infer_type(expr)?; + + // This has do be done because variables might be defined without + // explicit type annotation. For example, 'let a = 5;'. Thus, the + // symbol table has to be updated with this new type information. + if var_decl_sym.lit_type == LitTypeVariant::None { + var_decl_sym.lit_type = { + match expr_res_type { + // implicitly convert no-type-annotated byte-type into an integer + LitTypeVariant::U8 => LitTypeVariant::I32, + _ => expr_res_type + } + } + } + if + var_decl_sym.lit_type != expr_res_type + && !is_type_coalescing_possible(expr_res_type, var_decl_sym.lit_type) + { + return Err(SAError::TypeError( + SATypeError::AssignmentTypeMismatch { + expected: var_decl_sym.lit_type, + found: expr.result_type() + } + )); + } + Ok(()) + } + + pub fn type_check_arr_var_decl_stmt(sym: &mut Symbol, vals: &Vec) -> SAResult { + for expr in vals { + if expr.result_type() != sym.lit_type + && !is_type_coalescing_possible(expr.result_type(), sym.lit_type) { + return Err(SAError::TypeError( + SATypeError::AssignmentTypeMismatch { + expected: sym.lit_type, + found: expr.result_type() + }) + ); + } + } + if sym.lit_type == LitTypeVariant::None && !vals.is_empty() { + let array_type: LitTypeVariant = Self::infer_type(&vals[0])?; + sym.lit_type = array_type; + } + Ok(()) + } +} \ No newline at end of file diff --git a/src/symbol/function.rs b/src/symbol/function.rs index 2b7283c..e93cbfc 100644 --- a/src/symbol/function.rs +++ b/src/symbol/function.rs @@ -27,7 +27,7 @@ SOFTWARE. use std::collections::HashMap; use crate::types::LitTypeVariant; -use super::{StorageClass, SymbolTrait, Symtable}; +use super::{StorageClass, Symbol, SymbolTrait, Symtable}; /// Limit of local variables in a function. pub const LOCAL_LIMIT: usize = 1024; @@ -119,7 +119,7 @@ pub struct FunctionInfo { pub stack_size: i32, pub return_type: LitTypeVariant, /// Contains information about the variables defined locally in 'this' function - pub local_syms: LocalSymtable, + pub local_syms: Symtable, pub storage_class: StorageClass, pub params: Symtable } @@ -131,14 +131,15 @@ impl FunctionInfo { stack_size: i32, return_type: LitTypeVariant, storage_class: StorageClass, - params: Symtable + params: Symtable, + locals: Symtable ) -> Self { Self { name, func_id, stack_size, return_type, - local_syms: LocalSymtable::new(), + local_syms: locals, storage_class, params } diff --git a/src/symbol/symbol_table.rs b/src/symbol/symbol_table.rs index 1563718..6c938ff 100644 --- a/src/symbol/symbol_table.rs +++ b/src/symbol/symbol_table.rs @@ -84,6 +84,32 @@ impl Symtable { self.syms.get(idx) } + pub fn get_symbol_mut(&mut self, idx: usize) -> Option<&mut T> { + assert!( + idx < NSYMBOLS, + "value '{}' out of bounds for range '{}'", + idx, + self.counter + ); + self.syms.get_mut(idx) + } + + pub fn get_or_fail(&self, idx: usize) -> &T { + if let Some(sym) = self.get_symbol(idx) { + sym + } else { + panic!("Symbol at index '{}' not found in provided symbol table", idx); + } + } + + pub fn get_mut_or_fail(&mut self, idx: usize) -> &mut T { + if let Some(sym) = self.get_symbol_mut(idx) { + sym + } else { + panic!("Symbol at index '{}' not found in provided symbol table", idx); + } + } + pub fn remove_symbol(&mut self, index: usize) -> T { assert!(self.counter < NSYMBOLS); self.syms.remove(index) diff --git a/src/tokenizer/token_kind.rs b/src/tokenizer/token_kind.rs index bb0f3c3..9f9eb9e 100644 --- a/src/tokenizer/token_kind.rs +++ b/src/tokenizer/token_kind.rs @@ -136,11 +136,17 @@ pub enum TokenKind { T_QMARK, T_EOF, T_NONE, + + // these tokens are for error purpose. Not to be used + // for Token construction. + T_EXPR, // expression token + T_DTYPE // datatype } impl TokenKind { pub fn as_str(&self) -> &'static str { match self { + Self::T_INT_NUM => "integer value", Self::T_PLUS => "+", Self::T_INCR => "++", Self::T_PLUSEQ => "+=", @@ -176,6 +182,10 @@ impl TokenKind { Self::T_LBRACE => "{", Self::T_RBRACE => "}", Self::T_DOT => ".", + Self::T_LBRACKET => "[", + Self::T_RBRACKET => "]", + Self::T_EXPR => "expression", + Self::T_DTYPE => "data type", _ => "", } } diff --git a/src/tokenizer/tokenizer_impl.rs b/src/tokenizer/tokenizer_impl.rs index b88bc91..0d3bfaf 100644 --- a/src/tokenizer/tokenizer_impl.rs +++ b/src/tokenizer/tokenizer_impl.rs @@ -35,7 +35,7 @@ use lazy_static::lazy_static; lazy_static! { static ref KEYWORDS: HashMap<&'static str, TokenKind> = { let mut _keys: HashMap<&'static str, TokenKind> = HashMap::new(); - _keys.insert("for", TokenKind::KW_FOR); + _keys.insert("foreach", TokenKind::KW_FOR); _keys.insert("while", TokenKind::KW_WHILE); _keys.insert("loop", TokenKind::KW_LOOP); _keys.insert("integer", TokenKind::KW_INT); @@ -45,29 +45,12 @@ lazy_static! { _keys.insert("void", TokenKind::KW_VOID,); _keys.insert("const", TokenKind::KW_CONST,); _keys.insert("return", TokenKind::KW_RETURN,); - _keys.insert("do", TokenKind::KW_DO,); - _keys.insert("case", TokenKind::KW_CASE,); _keys.insert("break", TokenKind::KW_BREAK,); _keys.insert("continue", TokenKind::KW_CONTINUE); - _keys.insert("default", TokenKind::KW_DEFAULT); - _keys.insert("enum", TokenKind::KW_ENUM); - _keys.insert("goto", TokenKind::KW_GOTO); - _keys.insert("register", TokenKind::KW_REGISTER); - _keys.insert("sizeof", TokenKind::KW_SIZEOF); - _keys.insert("typedef", TokenKind::KW_TYPEDEF); - _keys.insert("volatile", TokenKind::KW_VOLATILE); - _keys.insert("struct", TokenKind::KW_STRUCT); - _keys.insert("union", TokenKind::KW_UNION); - _keys.insert("static", TokenKind::KW_STATIC); - _keys.insert("inline", TokenKind::KW_INLINE); _keys.insert("if", TokenKind::KW_IF); _keys.insert("else", TokenKind::KW_ELSE); - _keys.insert("unsigned", TokenKind::KW_UNSIGNED); - _keys.insert("signed", TokenKind::KW_SIGNED); _keys.insert("long", TokenKind::KW_LONG); _keys.insert("short", TokenKind::KW_SHORT); - _keys.insert("auto", TokenKind::KW_AUTO); - _keys.insert("switch", TokenKind::KW_SWITCH); _keys.insert("extern", TokenKind::KW_EXTERN); _keys.insert("let", TokenKind::KW_LET); _keys.insert("def", TokenKind::KW_DEF); @@ -389,7 +372,7 @@ impl Tokenizer { } else { let _value: i64 = number.parse::().unwrap(); token.kind = if (0..256).contains(&_value) { TokenKind::T_CHAR } - else if ((std::i32::MAX as i64)..std::i64::MAX).contains(&_value) { TokenKind::T_LONG_NUM } + else if ((i32::MAX as i64)..i64::MAX).contains(&_value) { TokenKind::T_LONG_NUM } else { TokenKind::T_INT_NUM } } token.lexeme = String::from(number); diff --git a/src/types.rs b/src/types.rs index ce85cdf..bc69cfa 100644 --- a/src/types.rs +++ b/src/types.rs @@ -55,15 +55,22 @@ pub enum LitType { /// First usize: Length of the array, second usize: Size of each /// element in the array - Array(usize, usize), + Array(LitTypeArray), Null, // null type None, // placeholder } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct LitTypeArray { + items_count: usize, + item_size: usize, + items_type: Box +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum LitTypeVariant { - I32 = 1, + I32, I64, I16, U8, @@ -167,7 +174,7 @@ impl LitType { Self::F32(_) => LitTypeVariant::F32, Self::Str(_, _) => LitTypeVariant::Str, Self::Void => LitTypeVariant::Void, - Self::Array(_, _) => LitTypeVariant::Array, + Self::Array(_) => LitTypeVariant::Array, _ => panic!("not a valid type to calculate variant of!"), } } @@ -272,7 +279,9 @@ pub fn convert_ast(node: &AST, to: LitTypeVariant) -> TypeConversionResult { result_type: to })), ASTOperation::AST_WIDEN, - to + to, + None, + None )); } Err(TypeConversionError::BigSizeToSmallSize{ @@ -283,7 +292,9 @@ pub fn convert_ast(node: &AST, to: LitTypeVariant) -> TypeConversionResult { lazy_static! { static ref TYPE_PRECEDENCE: std::collections::HashMap = { let mut typ: std::collections::HashMap = HashMap::new(); + typ.insert(LitTypeVariant::I64 as u8, 3); typ.insert(LitTypeVariant::I32 as u8, 2); + typ.insert(LitTypeVariant::I16 as u8, 1); typ.insert(LitTypeVariant::U8 as u8, 0); typ }; @@ -295,6 +306,7 @@ pub fn infer_type_from_expr(expr: &Expr) -> LitTypeVariant { Expr::Binary(bin) => { let left_type: LitTypeVariant = infer_type_from_expr(&bin.left); let right_type: LitTypeVariant = infer_type_from_expr(&bin.right); + if left_type == LitTypeVariant::Str || right_type == LitTypeVariant::Str { if bin.operation == ASTOperation::AST_ADD { return LitTypeVariant::Str; @@ -332,15 +344,19 @@ pub fn infer_type_from_expr(expr: &Expr) -> LitTypeVariant { } } -pub fn are_compatible_for_operation(left: &T, right: &T, op: ASTOperation) -> (bool, LitTypeVariant) { - let ltype = left.variant(); - let rtype = right.variant(); +pub fn are_compatible_for_operation( + left: &T, + right: &T, + op: ASTOperation +) -> (bool, LitTypeVariant) { + let ltype: LitTypeVariant = left.variant(); + let rtype: LitTypeVariant = right.variant(); if ltype == rtype { return (true, ltype); } - let mut larger_type = ltype; - let lsize = left.type_size(); - let rsize = right.type_size(); + let mut larger_type: LitTypeVariant = ltype; + let lsize: usize = left.type_size(); + let rsize: usize = right.type_size(); if rsize > lsize { larger_type = rtype; } @@ -389,6 +405,8 @@ pub fn modify_ast_node_type(node: &mut AST, to: LitTypeVariant) -> Option { })), ASTOperation::AST_WIDEN, to, + None, + None )); } // if we reach here, then types are incompatible