From c42575c1c6dda1d376fb93c5e8011b0a406ce48a Mon Sep 17 00:00:00 2001 From: Hydrostic Date: Sun, 31 May 2026 20:59:18 +0800 Subject: [PATCH] refactor(sema): Separated from ir --- src/backend/mod.rs | 17 +- src/err.rs | 6 +- src/ir/generator.rs | 324 ++++++++----------------------- src/main.rs | 13 +- src/sema/analyzer.rs | 449 +++++++++++++++++++++++++++++++++++++++++++ src/sema/err.rs | 35 ++++ src/sema/hir.rs | 106 ++++++++++ src/sema/mod.rs | 5 + src/sema/symbol.rs | 83 ++++++++ src/sema/types.rs | 51 +++++ 10 files changed, 829 insertions(+), 260 deletions(-) create mode 100644 src/sema/analyzer.rs create mode 100644 src/sema/err.rs create mode 100644 src/sema/hir.rs create mode 100644 src/sema/mod.rs create mode 100644 src/sema/symbol.rs create mode 100644 src/sema/types.rs diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 8d7d5c8..24796db 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -15,6 +15,7 @@ mod tests { use crate::utils::case_list::CaseList; use crate::utils::num_sequence::NumberSequence; use crate::ir::generator::Generator as IRGenerator; + use crate::sema::analyzer::Analyzer; pub use super::generator::Generator as ASMGenerator; fn test_case(case_str: &str) { let case_sequence = NumberSequence::from_str(case_str).unwrap(); @@ -49,12 +50,14 @@ mod tests { parser.diagnostics.print(&format!("{}", case_path.display()), &full_text); is_error = true; } - let mut generator = IRGenerator::new(); - let ir = generator.emit(compile_unit); - // if !generator.diagnostic.is_empty() { - // generator.diagnostic.print(&format!("{}", case_path.display()), &full_text); - // is_error = true; - // } + let mut analyzer = Analyzer::new(); + let hir = analyzer.analyze(compile_unit); + if !analyzer.get_diagnostics().is_empty() { + analyzer.get_diagnostics().print(&format!("{}", case_path.display()), &full_text); + is_error = true; + } + let mut generator = IRGenerator::new(&analyzer); + let ir = generator.emit(hir); let mut asm_generator = ASMGenerator::new(); asm_generator.emit(ir); let asm_text = asm_generator.to_text(); @@ -82,4 +85,4 @@ mod tests { fn test_func() { test_case("12-13,58-60"); } -} \ No newline at end of file +} diff --git a/src/err.rs b/src/err.rs index 9f4869f..74b1a5b 100644 --- a/src/err.rs +++ b/src/err.rs @@ -1,9 +1,11 @@ use thiserror::Error; -use crate::frontend::err::FrontendError; +use crate::{frontend::err::FrontendError, sema::err::SemaError}; #[derive(Debug, Clone, PartialEq, Eq, Error)] pub enum CompileError { #[error(transparent)] Frontend(#[from] FrontendError), -} \ No newline at end of file + #[error(transparent)] + Sema(#[from] SemaError), +} diff --git a/src/ir/generator.rs b/src/ir/generator.rs index ea6bebb..2ea9ebf 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -1,13 +1,11 @@ -use std::{collections::{BTreeMap, BTreeSet}, vec}; +use std::{collections::BTreeMap, vec}; -use crate::{ast::types::{BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt}, diagnostic::Diagnositics, ir::{err::IRError, types::{BinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}}}; +use crate::{ir::types::{BinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirReturnStmt, HirStatement, HirVarDeclStmt, HirWhileStmt}, symbol::{FunctionId, SymbolId}}}; use crate::ast::types::BinaryOp as AstBinaryOp; use crate::ast::types::UnaryOp as AstUnaryOp; -pub struct Generator { +pub struct Generator<'a> { + sema: &'a SemaAnalyzer, var_manager: VariableManager, - function_map: BTreeMap, - current_func_return_type: Option, - diagnostic: Diagnositics, current_exit_label: Vec>, // true exit, false exit // About exit label passing: // for non-logical parent expr, current_exit_label is None @@ -19,24 +17,11 @@ pub struct Generator { label_counter: usize } -impl Generator { - pub fn new() -> Self { - let mut function_map = BTreeMap::new(); - function_map.insert("putint".to_string(), Function { - name: "putint".to_string(), - parameter_types: vec![IRType::I32], - return_type: IRType::Void, - }); - function_map.insert("getint".to_string(), Function { - name: "getint".to_string(), - parameter_types: vec![], - return_type: IRType::I32, - }); +impl<'a> Generator<'a> { + pub fn new(sema: &'a SemaAnalyzer) -> Self { Self { + sema, var_manager: VariableManager::new(), - current_func_return_type: None, - diagnostic: Diagnositics::new(), - function_map, current_exit_label: vec![], while_exit_label: vec![], func_exit: None, @@ -48,15 +33,22 @@ impl Generator { self.label_counter += 1; label } - pub fn emit(&mut self, compile_unit: CompileUnit) -> Vec { + pub fn emit(&mut self, compile_unit: HirCompileUnit) -> Vec { self.generate_compile_unit(compile_unit) } - pub fn get_diagnostics(&self) -> &Diagnositics { - &self.diagnostic + + fn ir_function(&self, function_id: FunctionId) -> Function { + let sig = self.sema.get_function_sig(function_id); + Function { + name: sig.name.clone(), + parameter_types: sig.parameter_types.iter().map(|ty| (*ty).into()).collect(), + return_type: sig.return_type.into(), + } } - fn generate_compile_unit(&mut self, compile_unit: CompileUnit) -> Vec { + + fn generate_compile_unit(&mut self, compile_unit: HirCompileUnit) -> Vec { let mut instrs = vec![]; - use GlobalDeclStmt::*; + use HirGlobalDeclStmt::*; for decl in compile_unit.global_decls { match decl { VarDecl(var_decl) => { @@ -70,48 +62,29 @@ impl Generator { instrs } - fn generate_var_decl(&mut self, var_decl: VarDeclStmt, is_global: bool) -> Vec { + fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec { let mut instrs = vec![]; let var_type = if is_global { VariableType::Global } else { VariableType::Local }; for value in var_decl.values { - match self.var_manager.declare_variable(&value.name, var_type, var_decl.data_type.into()) { - Ok(var) => { - if is_global { instrs.push(IRInstr::Declare(var)); } - } - Err(e) => { - self.diagnostic.add_from_ir_error(e, value.name_span); - } + let var = self.var_manager.declare_symbol(value.symbol, var_type, var_decl.data_type.into()); + if is_global { + instrs.push(IRInstr::Declare(var)); } } instrs } - fn generate_func_decl(&mut self, func_decl: FuncDeclStmt) -> Vec { - if self.function_map.contains_key(&func_decl.name) { - self.diagnostic.add_from_ir_error(IRError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span); - return vec![]; - } - self.current_func_return_type = Some(func_decl.return_type.into()); - self.var_manager.enter_scope(); - let parameters: Vec = match func_decl.params.iter().map(|param| { - match self.var_manager.declare_variable(¶m.name, VariableType::Local, param.param_type.into()) { - Ok(var) => Ok(var), - Err(e) => { - self.diagnostic.add_from_ir_error(e, param.name_span); - Err(()) - } - } - }).collect() { - Ok(p) => p, - Err(()) => return vec![], - }; + fn generate_func_decl(&mut self, func_decl: HirFuncDeclStmt) -> Vec { + let parameters: Vec = func_decl.params.iter() + .map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.into())) + .collect(); let temp_parameters = parameters.iter().enumerate() .map(|(i, param)| self.var_manager.declare_param_temp(param.data_type, i)) .collect::>(); let mut body_instrs = vec![]; self.func_exit = Some((self.request_label(), { - let ret_type = func_decl.return_type.into(); + let ret_type = func_decl.sig.return_type.into(); if ret_type != IRType::Void { Some(self.var_manager.declare_unamed_local(ret_type)) } else { @@ -133,18 +106,12 @@ impl Generator { let func_exit = self.func_exit.take().unwrap(); body_instrs.push(IRInstr::Label(func_exit.0)); body_instrs.push(IRInstr::Exit(func_exit.1)); - self.var_manager.exit_scope(); - self.current_func_return_type = None; self.var_manager.clear_local_counter(); - let func = Function { - name: func_decl.name, - parameter_types: temp_parameters.iter().map(|v| v.data_type).collect(), - return_type: func_decl.return_type.into(), - }; - self.function_map.insert(func.name.clone(), func.clone()); + let mut func = self.ir_function(func_decl.sig.id); + func.parameter_types = temp_parameters.iter().map(|v| v.data_type).collect(); vec![IRInstr::DefineFunc(func, temp_parameters, body_instrs)] } - fn generate_block_stmt(&mut self, block_stmt: BlockStmt) -> Vec { + fn generate_block_stmt(&mut self, block_stmt: HirBlockStmt) -> Vec { let mut instrs = vec![]; for stmt in block_stmt.statements { instrs.extend(self.generate_statement(stmt)); @@ -152,8 +119,8 @@ impl Generator { instrs } - fn generate_statement(&mut self, stmt: Statement) -> Vec { - use Statement::*; + fn generate_statement(&mut self, stmt: HirStatement) -> Vec { + use HirStatement::*; let instrs = match stmt { Return(return_stmt) => self.generate_return_stmt(return_stmt), If(if_stmt) => self.generate_if_stmt(if_stmt), @@ -161,10 +128,7 @@ impl Generator { Break(break_stmt) => self.generate_break_stmt(break_stmt), Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt), Block(block_stmt) => { - self.var_manager.enter_scope(); - let block_instrs = self.generate_block_stmt(block_stmt); - self.var_manager.exit_scope(); - block_instrs + self.generate_block_stmt(block_stmt) }, Expr(expr) => { self.current_exit_label.push(None); @@ -183,16 +147,11 @@ impl Generator { instrs } - fn generate_return_stmt(&mut self, return_stmt: ReturnStmt) -> Vec { + fn generate_return_stmt(&mut self, return_stmt: HirReturnStmt) -> Vec { let mut instrs = vec![]; let func_exit = self.func_exit.unwrap(); match return_stmt.value { Some(expr) => { - if func_exit.1.is_none() { - // shouldn't return value but return stmt has expr; - self.diagnostic.add_from_ir_error(IRError::ReturnExpressionOnVoidFunction, return_stmt.span); - return vec![]; - } self.current_exit_label.push(None); let (value_instrs, value_var) = match self.generate_expr(expr) { Some(res) => res, @@ -202,37 +161,21 @@ impl Generator { } }; self.current_exit_label.pop(); - if value_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), return_stmt.span); - self.current_exit_label.pop(); - return vec![]; - } instrs.extend(value_instrs); instrs.push(IRInstr::Move(func_exit.1.unwrap(), MoveRValue::Var(value_var.unwrap()))); - // if value_var.is_none() { - // self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), return_stmt.span); - // return vec![]; - // } - // let value_var = value_var.unwrap(); } - None => { - if func_exit.1.is_some() { - // should return value but return stmt has no expr; - self.diagnostic.add_from_ir_error(IRError::TypeMismatch(self.current_func_return_type.unwrap(), IRType::Void), return_stmt.span); - } - }, + None => {}, } instrs.push(IRInstr::Goto(func_exit.0)); instrs } - fn generate_while_stmt(&mut self, while_stmt: WhileStmt) -> Vec { + fn generate_while_stmt(&mut self, while_stmt: HirWhileStmt) -> Vec { let mut instrs = vec![]; let cond_label = self.request_label(); let body_label = self.request_label(); let exit_label = self.request_label(); instrs.push(IRInstr::Label(cond_label)); self.current_exit_label.push(Some((body_label, exit_label))); - let while_cond_span = while_stmt.condition.span; let (cond_instrs, cond_var) = match self.generate_expr(while_stmt.condition) { Some(res) => res, None => { @@ -241,10 +184,6 @@ impl Generator { } }; self.current_exit_label.pop(); - if cond_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), while_cond_span); - return vec![]; - } instrs.extend(cond_instrs); instrs.push(IRInstr::Label(body_label)); self.while_exit_label.push((cond_label, exit_label)); @@ -254,7 +193,7 @@ impl Generator { instrs.push(IRInstr::Label(exit_label)); instrs } - fn generate_if_stmt(&mut self, if_stmt: IfStmt) -> Vec { + fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec { let mut instrs = vec![]; let then_label = self.request_label(); let exit_label = self.request_label(); @@ -273,7 +212,6 @@ impl Generator { labels.push(exit_label); // now generate if expr, true exit to labels[0], false exit to labels[1] self.current_exit_label.push(Some((labels[0], labels[1]))); - let cond_span = if_stmt.condition.span; let (cond_instrs, cond_var) = match self.generate_expr(if_stmt.condition) { Some(res) => res, None => { @@ -282,19 +220,14 @@ impl Generator { } }; self.current_exit_label.pop(); - if cond_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), cond_span); - return vec![]; - } instrs.extend(cond_instrs); instrs.push(IRInstr::Label(labels[0])); instrs.extend(self.generate_block_stmt(if_stmt.then_branch)); instrs.push(IRInstr::Goto(exit_label)); for (i, else_if_branch) in if_stmt.ifelse_branch.into_iter().enumerate() { - let IfElseBranch { condition: else_if_cond, then_branch: else_if_block } = else_if_branch; + let HirIfElseBranch { condition: else_if_cond, then_branch: else_if_block } = else_if_branch; instrs.push(IRInstr::Label(labels[i * 2 + 1])); self.current_exit_label.push(Some((labels[i * 2 + 2], labels[i * 2 + 3]))); - let else_if_cond_span = else_if_cond.span; let (else_if_cond_instrs, else_if_cond_var) = match self.generate_expr(else_if_cond) { Some(res) => res, None => { @@ -303,10 +236,6 @@ impl Generator { } }; self.current_exit_label.pop(); - if else_if_cond_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), else_if_cond_span); - return vec![]; - } instrs.extend(else_if_cond_instrs); instrs.push(IRInstr::Label(labels[i * 2 + 2])); instrs.extend(self.generate_block_stmt(else_if_block)); @@ -320,71 +249,47 @@ impl Generator { instrs.push(IRInstr::Label(exit_label)); instrs } - fn generate_continue_stmt(&mut self, stmt: ContinueStmt) -> Vec { + fn generate_continue_stmt(&mut self, _stmt: HirContinueStmt) -> Vec { if let Some((continue_label, _)) = self.while_exit_label.last() { vec![IRInstr::Goto(*continue_label)] } else { - self.diagnostic.add_from_ir_error(IRError::ContinueOutsideLoop, stmt.span); vec![] } } - fn generate_break_stmt(&mut self, stmt: BreakStmt) -> Vec { + fn generate_break_stmt(&mut self, _stmt: HirBreakStmt) -> Vec { if let Some((_, break_label)) = self.while_exit_label.last() { vec![IRInstr::Goto(*break_label)] } else { - self.diagnostic.add_from_ir_error(IRError::BreakOutsideLoop, stmt.span); vec![] } } - fn generate_expr(&mut self, expr: Expr) -> Option<(Vec, Option)> { + fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec, Option)> { // there may be some expr that doesn't produce value, like void func call let (mut instrs, var) = match expr.value { - ExprValue::IntLit(i) => { + HirExprValue::IntLit(i) => { // TODO: convert check let var = self.var_manager.declare_temp(IRType::I32); (vec![IRInstr::Move(var, MoveRValue::ConstInt(i as i32))], Some(var)) }, - ExprValue::Var(name) => { - if let Some(var) = self.var_manager.get_variable(&name) { - (vec![], Some(var)) - } else { - self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), expr.span); - return None; - } + HirExprValue::Var(symbol) => { + let var = self.var_manager.get_symbol(symbol).unwrap(); + (vec![], Some(var)) }, - ExprValue::Assign { lvalue, rvalue } => { - // TODO: only support simple variable assignment now, need to support more complex lvalue in the future - if let ExprValue::Var(name) = lvalue.value { - let var = match self.var_manager.get_variable(&name) { - Some(var) => var, - None => { - self.diagnostic.add_from_ir_error(IRError::VariableNotFound(name.clone()), lvalue.span); - return None; - } - }; - self.current_exit_label.push(None); - let rvalue_span = rvalue.span; - let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?; - self.current_exit_label.pop(); - // TODO: further check - // if var.data_type != rvalue_var.data_type { - // self.diagnostic.add_from_ir_error(IRError::TypeMismatch(var.data_type, rvalue_var.data_type), lvalue.span); - // return (vec![], None); - // } - if rvalue_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rvalue_span); - return None; - } - instrs.push(IRInstr::Move(var, MoveRValue::Var(rvalue_var.unwrap()))); - let temp_var = self.var_manager.declare_temp(var.data_type); - instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(var))); - (instrs, Some(temp_var)) + HirExprValue::Assign { lvalue, rvalue } => { + let var = if let HirExprValue::Var(symbol) = lvalue.value { + self.var_manager.get_symbol(symbol).unwrap() } else { - self.diagnostic.add_from_ir_error(IRError::InvalidAssignmentTarget, lvalue.span); return None; - } + }; + self.current_exit_label.push(None); + let (mut instrs, rvalue_var) = self.generate_expr(*rvalue)?; + self.current_exit_label.pop(); + instrs.push(IRInstr::Move(var, MoveRValue::Var(rvalue_var.unwrap()))); + let temp_var = self.var_manager.declare_temp(var.data_type); + instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(var))); + (instrs, Some(temp_var)) }, - ExprValue::UnaryOp { op, operand } => { + HirExprValue::UnaryOp { op, operand } => { let mut parent_is_logical = true; let exit_passdown = if matches!(op, AstUnaryOp::Not) { Some(self.current_exit_label.last().cloned().flatten().map_or_else(|| { @@ -409,13 +314,8 @@ impl Generator { self.current_exit_label.push(exit_passdown); - let operand_span = operand.span; let (mut instrs, operand_var) = self.generate_expr(*operand)?; self.current_exit_label.pop(); - if operand_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), operand_span); - return None; - } let operand_var = operand_var.unwrap(); let dest_var = match op { AstUnaryOp::Add => { @@ -449,10 +349,7 @@ impl Generator { }; (instrs, Some(dest_var)) }, - ExprValue::BinaryOp { lhs, op, rhs } => { - let lhs_span = lhs.span; - let rhs_span = rhs.span; - + HirExprValue::BinaryOp { lhs, op, rhs } => { // +--------+-------------------------+-------------------------+-------------------------+ // | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) | // | self | and(next) | or(next) | others | @@ -494,18 +391,10 @@ impl Generator { self.current_exit_label.push(lhs_exit_passdown); let (mut instrs, left_var) = self.generate_expr(*lhs)?; self.current_exit_label.pop(); - if left_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), lhs_span); - return None; - } let mut left_var = left_var.unwrap(); self.current_exit_label.push(rhs_exit_passdown); let (right_instrs, right_var) = self.generate_expr(*rhs)?; self.current_exit_label.pop(); - if right_var.is_none() { - self.diagnostic.add_from_ir_error(IRError::InvalidOperand(IRType::Void), rhs_span); - return None; - } let mut right_var = right_var.unwrap(); // check implicit convert let convert_to; @@ -513,13 +402,7 @@ impl Generator { // we dont really care since we use cmp convert_to = left_var.data_type; } else { - if let Some(ty) = IRType::get_elevate_result(left_var.data_type, right_var.data_type) { - convert_to = ty; - } else { - self.diagnostic.add_from_ir_error(IRError::IncompatiableOperand(left_var.data_type, right_var.data_type), lhs_span); - self.diagnostic.add_from_ir_error(IRError::IncompatiableOperand(left_var.data_type, right_var.data_type), rhs_span); - return None; - } + convert_to = IRType::get_elevate_result(left_var.data_type, right_var.data_type).unwrap(); } // do implicit convert if needed // TODO: further check @@ -606,50 +489,18 @@ impl Generator { // } (instrs, Some(dest_var)) }, - ExprValue::FuncCall(func_name, args) => { + HirExprValue::FuncCall(func_id, args) => { let mut instrs = vec![]; let mut arg_vars = vec![]; - let func_def = if let Some(func_def) = self.function_map.get(&func_name) { - func_def - } else { - self.diagnostic.add_from_ir_error(IRError::FunctionNotFound(func_name.clone()), expr.span); - return None; - }.clone(); + let func_def = self.ir_function(func_id); - if args.len() < func_def.parameter_types.len() { - self.diagnostic.add_from_ir_error(IRError::TooFewArguments(func_def.parameter_types.len(), args.len()), expr.span); - return None; - } - if args.len() > func_def.parameter_types.len() { - self.diagnostic.add_from_ir_error(IRError::TooManyArguments(func_def.parameter_types.len(), args.len()), expr.span); - return None; - } - let mut has_error = false; - for parameter_type in &func_def.parameter_types { - if matches!(parameter_type, IRType::Void) { - self.diagnostic.add_from_ir_error(IRError::InvalidParameterType(IRType::Void), expr.span); - has_error = true; - } - } - if has_error { - return None; - } - for (i, arg) in args.into_iter().enumerate() { + for arg in args.into_iter() { self.current_exit_label.push(None); let (arg_instrs, arg_var) = self.generate_expr(arg)?; self.current_exit_label.pop(); - let parameter_type = func_def.parameter_types.get(i).unwrap(); - if *parameter_type != arg_var.map_or(IRType::Void, |v| v.data_type) { - self.diagnostic.add_from_ir_error(IRError::TypeMismatch(*parameter_type, arg_var.map_or(IRType::Void, |v| v.data_type)), expr.span); - has_error = true; - continue; - } instrs.extend(arg_instrs); arg_vars.push(arg_var.unwrap()); } - if has_error { - return None; - } let ret_variable = if matches!(func_def.return_type, IRType::Void) { None } else { @@ -671,8 +522,7 @@ impl Generator { } struct VariableManager { - variable_map: BTreeMap>, - scopes: Vec>, + variable_map: BTreeMap, global_counter: usize, local_counter: usize, local_var_type: Vec, @@ -682,27 +532,12 @@ impl VariableManager { pub fn new() -> Self { Self { variable_map: BTreeMap::new(), - scopes: vec![BTreeSet::new()], global_counter: 0, local_counter: 0, local_var_type: vec![], } } - pub fn enter_scope(&mut self) { - self.scopes.push(BTreeSet::new()); - } - - pub fn exit_scope(&mut self) { - let variables = self.scopes.pop().unwrap(); - for var in variables { - self.variable_map.get_mut(&var).unwrap().pop(); - } - } - - fn declare_variable(&mut self, name: &str, var_type: VariableType, var_data_type: IRType) -> Result { - if self.scopes.last().unwrap().contains(name) { - return Err(IRError::VariableHasBeenDefined(name.to_string())); - } + fn declare_symbol(&mut self, symbol: SymbolId, var_type: VariableType, var_data_type: IRType) -> Variable { let variable = match var_type { VariableType::Global => { let var = Variable { index: self.global_counter, var_type, data_type: var_data_type }; @@ -716,18 +551,11 @@ impl VariableManager { } _ => unreachable!(), }; - self.variable_map.entry(name.to_string()).or_default().push(variable); - self.scopes.last_mut().unwrap().insert(name.to_string()); + self.variable_map.insert(symbol, variable); if matches!(var_type, VariableType::Local) { self.local_var_type.push(variable); } - Ok(variable) - } - pub fn declare_gloabal(&mut self, name: &str, var_data_type: IRType) -> Result { - self.declare_variable(name, VariableType::Global, var_data_type) - } - pub fn declare_local(&mut self, name: &str, var_data_type: IRType) -> Result { - self.declare_variable(name, VariableType::Local, var_data_type) + variable } pub fn declare_temp(&mut self, var_data_type: IRType) -> Variable { let var = Variable { index: self.local_counter, var_type: VariableType::Temp, data_type: var_data_type }; @@ -750,12 +578,13 @@ impl VariableManager { pub fn clear_local_counter(&mut self) { self.local_counter = 0; self.local_var_type.clear(); + self.variable_map.retain(|_, var| matches!(var.var_type, VariableType::Global)); } pub fn get_cur_func_variables(&self) -> Vec { self.local_var_type.iter().cloned().collect() } - pub fn get_variable(&self, name: &str) -> Option { - self.variable_map.get(name).and_then(|vars| vars.last()).cloned() + pub fn get_symbol(&self, symbol: SymbolId) -> Option { + self.variable_map.get(&symbol).cloned() } } @@ -769,6 +598,7 @@ mod tests { use std::io::Write; use crate::frontend::lexer::Lexer; use crate::frontend::parser::Parser; + use crate::sema::analyzer::Analyzer; use crate::utils::case_list::CaseList; use crate::utils::num_sequence::NumberSequence; @@ -806,12 +636,14 @@ mod tests { parser.diagnostics.print(&format!("{}", case_path.display()), &full_text); is_error = true; } - let mut generator = Generator::new(); - let ir = generator.emit(compile_unit); - if !generator.diagnostic.is_empty() { - generator.diagnostic.print(&format!("{}", case_path.display()), &full_text); + let mut analyzer = Analyzer::new(); + let hir = analyzer.analyze(compile_unit); + if !analyzer.get_diagnostics().is_empty() { + analyzer.get_diagnostics().print(&format!("{}", case_path.display()), &full_text); is_error = true; } + let mut generator = Generator::new(&analyzer); + let ir = generator.emit(hir); let mut ir_file = File::create(format!("output/{}.ir", case_name)).unwrap(); for instr in ir { writeln!(ir_file, "{}", instr).unwrap(); @@ -838,4 +670,4 @@ mod tests { fn test_func() { test_case("12-13,58-60"); } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 1046d9f..f3d9f40 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,12 +5,13 @@ mod backend; mod utils; mod diagnostic; mod err; +mod sema; use std::{fs::File, io::BufRead}; use clap::Parser as ArgParser; -use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator}; +use crate::{frontend::{lexer::Lexer, parser::Parser}, ir::generator::Generator, sema::analyzer::Analyzer}; use crate::backend::generator::Generator as ASMGerenerator; /// Simple minic compiler built by Rust #[derive(ArgParser, Debug)] @@ -78,11 +79,13 @@ fn main() { if !parser.diagnostics.is_empty() { parser.diagnostics.print(&format!("{}", source_path.display()), &full_text); } - let mut generator = Generator::new(); - let ir = generator.emit(compile_unit); - if !generator.get_diagnostics().is_empty() { - generator.get_diagnostics().print(&format!("{}", source_path.display()), &full_text); + let mut analyzer = Analyzer::new(); + let hir = analyzer.analyze(compile_unit); + if !analyzer.get_diagnostics().is_empty() { + analyzer.get_diagnostics().print(&format!("{}", source_path.display()), &full_text); } + let mut generator = Generator::new(&analyzer); + let ir = generator.emit(hir); if args.output_ir { if let Some(output_path) = args.output { match std::fs::write(&output_path, ir.iter().map(|instr| instr.to_string()).collect::>().join("\n")) { diff --git a/src/sema/analyzer.rs b/src/sema/analyzer.rs new file mode 100644 index 0000000..c60acec --- /dev/null +++ b/src/sema/analyzer.rs @@ -0,0 +1,449 @@ +use std::collections::BTreeMap; + +use crate::{ + ast::types::{ + BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, + GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt, + }, + diagnostic::{span::Span, Diagnositics}, + sema::{ + err::SemaError, + hir::{ + HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, + HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam, + HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt, + }, + symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable}, + types::SemaType, + }, +}; + +pub struct Analyzer { + symbols: SymbolTable, + function_map: BTreeMap, + functions: Vec, + current_func_return_type: Option, + diagnostic: Diagnositics, + loop_depth: usize, +} + +impl Analyzer { + pub fn new() -> Self { + let mut analyzer = Self { + symbols: SymbolTable::new(), + function_map: BTreeMap::new(), + functions: vec![], + current_func_return_type: None, + diagnostic: Diagnositics::new(), + loop_depth: 0, + }; + analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void); + analyzer.declare_builtin_func("getint", vec![], SemaType::I32); + analyzer + } + + pub fn analyze(&mut self, compile_unit: CompileUnit) -> HirCompileUnit { + self.analyze_compile_unit(compile_unit) + } + + pub fn get_diagnostics(&self) -> &Diagnositics { + &self.diagnostic + } + + pub fn get_symbol_type(&self, symbol: SymbolId) -> SemaType { + self.symbols.get_symbol(symbol).ty + } + + pub fn get_symbol_kind(&self, symbol: SymbolId) -> SymbolKind { + self.symbols.get_symbol(symbol).kind + } + + pub fn get_function_sig(&self, function: FunctionId) -> &FunctionSig { + &self.functions[function.0] + } + + fn declare_builtin_func(&mut self, name: &str, parameter_types: Vec, return_type: SemaType) { + let id = FunctionId(self.functions.len()); + let sig = FunctionSig { + id, + name: name.to_string(), + return_type, + parameter_types, + }; + self.function_map.insert(name.to_string(), id); + self.functions.push(sig); + } + + fn add_error(&mut self, error: SemaError, span: Span) { + self.diagnostic.add_from_error(error, span); + } + + fn analyze_compile_unit(&mut self, compile_unit: CompileUnit) -> HirCompileUnit { + let mut global_decls = vec![]; + for decl in compile_unit.global_decls { + match decl { + GlobalDeclStmt::VarDecl(var_decl) => { + global_decls.push(HirGlobalDeclStmt::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Global))); + } + GlobalDeclStmt::FuncDecl(func_decl) => { + if let Some(func_decl) = self.analyze_func_decl(func_decl) { + global_decls.push(HirGlobalDeclStmt::FuncDecl(func_decl)); + } + } + } + } + HirCompileUnit { global_decls } + } + + fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt { + let data_type = var_decl.data_type.into(); + let mut values = vec![]; + for value in var_decl.values { + match self.symbols.declare_variable(&value.name, kind, data_type) { + Ok(symbol) => values.push(HirVarDeclStmtValue { + symbol, + name_span: value.name_span, + }), + Err(e) => self.add_error(e, value.name_span), + } + } + HirVarDeclStmt { + values, + data_type, + type_span: var_decl.type_span, + } + } + + fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option { + if self.function_map.contains_key(&func_decl.name) { + self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span); + return None; + } + + let function_id = FunctionId(self.functions.len()); + let parameter_types = func_decl.params.iter().map(|param| param.param_type.into()).collect::>(); + let return_type = func_decl.return_type.into(); + let sig = FunctionSig { + id: function_id, + name: func_decl.name.clone(), + return_type, + parameter_types, + }; + self.function_map.insert(func_decl.name.clone(), function_id); + self.functions.push(sig.clone()); + + self.current_func_return_type = Some(return_type); + self.symbols.enter_scope(); + + let mut params = vec![]; + for param in func_decl.params { + let param_type = param.param_type.into(); + match self.symbols.declare_variable(¶m.name, SymbolKind::Param, param_type) { + Ok(symbol) => params.push(HirParam { + symbol, + param_type, + name_span: param.name_span, + type_span: param.type_span, + }), + Err(e) => self.add_error(e, param.name_span), + } + } + + let body = self.analyze_block_stmt(func_decl.body); + self.symbols.exit_scope(); + self.current_func_return_type = None; + + Some(HirFuncDeclStmt { + sig, + params, + body, + ret_type_span: func_decl.ret_type_span, + name_span: func_decl.name_span, + }) + } + + fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt { + let mut statements = vec![]; + for stmt in block_stmt.statements { + if let Some(stmt) = self.analyze_statement(stmt) { + statements.push(stmt); + } + } + HirBlockStmt { statements } + } + + fn analyze_statement(&mut self, stmt: Statement) -> Option { + match stmt { + Statement::Return(stmt) => Some(HirStatement::Return(self.analyze_return_stmt(stmt))), + Statement::If(stmt) => Some(HirStatement::If(self.analyze_if_stmt(stmt))), + Statement::While(stmt) => Some(HirStatement::While(self.analyze_while_stmt(stmt))), + Statement::Break(stmt) => Some(HirStatement::Break(self.analyze_break_stmt(stmt))), + Statement::Continue(stmt) => Some(HirStatement::Continue(self.analyze_continue_stmt(stmt))), + Statement::Block(stmt) => { + self.symbols.enter_scope(); + let block = self.analyze_block_stmt(stmt); + self.symbols.exit_scope(); + Some(HirStatement::Block(block)) + } + Statement::Expr(expr) => self.analyze_expr(expr).map(HirStatement::Expr), + Statement::VarDecl(var_decl) => Some(HirStatement::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Local))), + } + } + + fn analyze_return_stmt(&mut self, return_stmt: ReturnStmt) -> HirReturnStmt { + let expected_ty = self.current_func_return_type.unwrap(); + let value = match return_stmt.value { + Some(expr) => { + if expected_ty == SemaType::Void { + self.add_error(SemaError::ReturnExpressionOnVoidFunction, return_stmt.span); + None + } else { + match self.analyze_expr(expr) { + Some(expr) => { + if expr.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), return_stmt.span); + } else if expr.ty != expected_ty { + self.add_error(SemaError::TypeMismatch(expected_ty, expr.ty), return_stmt.span); + } + Some(expr) + } + None => None, + } + } + } + None => { + if expected_ty != SemaType::Void { + self.add_error(SemaError::TypeMismatch(expected_ty, SemaType::Void), return_stmt.span); + } + None + } + }; + HirReturnStmt { + value, + span: return_stmt.span, + } + } + + fn analyze_if_stmt(&mut self, if_stmt: IfStmt) -> HirIfStmt { + let condition = self.analyze_condition_expr(if_stmt.condition); + let then_branch = self.analyze_block_stmt(if_stmt.then_branch); + let mut ifelse_branch = vec![]; + for branch in if_stmt.ifelse_branch { + let IfElseBranch { condition, then_branch } = branch; + ifelse_branch.push(HirIfElseBranch { + condition: self.analyze_condition_expr(condition), + then_branch: self.analyze_block_stmt(then_branch), + }); + } + let else_branch = if_stmt.else_branch.map(|block| self.analyze_block_stmt(block)); + HirIfStmt { + condition, + then_branch, + ifelse_branch, + else_branch, + } + } + + fn analyze_while_stmt(&mut self, while_stmt: WhileStmt) -> HirWhileStmt { + let condition = self.analyze_condition_expr(while_stmt.condition); + self.loop_depth += 1; + let body = self.analyze_block_stmt(while_stmt.body); + self.loop_depth -= 1; + HirWhileStmt { + condition, + body, + } + } + + fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt { + if self.loop_depth == 0 { + self.add_error(SemaError::BreakOutsideLoop, stmt.span); + } + HirBreakStmt { span: stmt.span } + } + + fn analyze_continue_stmt(&mut self, stmt: ContinueStmt) -> HirContinueStmt { + if self.loop_depth == 0 { + self.add_error(SemaError::ContinueOutsideLoop, stmt.span); + } + HirContinueStmt { span: stmt.span } + } + + fn analyze_condition_expr(&mut self, expr: Expr) -> HirExpr { + let span = expr.span; + match self.analyze_expr(expr) { + Some(expr) => { + if expr.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), span); + } + expr + } + None => HirExpr { + value: HirExprValue::IntLit(0), + ty: SemaType::I32, + span, + }, + } + } + + fn analyze_expr(&mut self, expr: Expr) -> Option { + let span = expr.span; + match expr.value { + ExprValue::IntLit(value) => Some(HirExpr { + value: HirExprValue::IntLit(value), + ty: SemaType::I32, + span, + }), + ExprValue::Var(name) => { + let symbol = match self.symbols.get_variable(&name) { + Some(symbol) => symbol, + None => { + self.add_error(SemaError::VariableNotFound(name), span); + return None; + } + }; + Some(HirExpr { + value: HirExprValue::Var(symbol), + ty: self.symbols.get_symbol(symbol).ty, + span, + }) + } + ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span), + ExprValue::UnaryOp { op, operand } => { + let operand_span = operand.span; + let operand = self.analyze_expr(*operand)?; + if operand.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), operand_span); + return None; + } + let ty = match op { + crate::ast::types::UnaryOp::Not => SemaType::I1, + _ => operand.ty, + }; + Some(HirExpr { + value: HirExprValue::UnaryOp { + op, + operand: Box::new(operand), + }, + ty, + span, + }) + } + ExprValue::BinaryOp { lhs, op, rhs } => self.analyze_binary_expr(*lhs, op, *rhs, span), + ExprValue::FuncCall(func_name, args) => self.analyze_func_call_expr(func_name, args, span), + } + } + + fn analyze_assign_expr(&mut self, lvalue: Expr, rvalue: Expr, span: Span) -> Option { + if !matches!(lvalue.value, ExprValue::Var(_)) { + self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span); + return None; + } + let lvalue = self.analyze_expr(lvalue)?; + let rvalue_span = rvalue.span; + let rvalue = self.analyze_expr(rvalue)?; + if rvalue.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), rvalue_span); + return None; + } + Some(HirExpr { + ty: lvalue.ty, + value: HirExprValue::Assign { + lvalue: Box::new(lvalue), + rvalue: Box::new(rvalue), + }, + span, + }) + } + + fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option { + let lhs_span = lhs.span; + let rhs_span = rhs.span; + let lhs = self.analyze_expr(lhs)?; + let rhs = self.analyze_expr(rhs)?; + if lhs.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), lhs_span); + return None; + } + if rhs.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), rhs_span); + return None; + } + + let result_ty = if op.is_logical() { + SemaType::I1 + } else { + match SemaType::get_elevate_result(lhs.ty, rhs.ty) { + Some(_) if op.is_cmp() => SemaType::I1, + Some(ty) => ty, + None => { + self.add_error(SemaError::IncompatiableOperand(lhs.ty, rhs.ty), lhs_span); + self.add_error(SemaError::IncompatiableOperand(lhs.ty, rhs.ty), rhs_span); + return None; + } + } + }; + + Some(HirExpr { + value: HirExprValue::BinaryOp { + lhs: Box::new(lhs), + op, + rhs: Box::new(rhs), + }, + ty: result_ty, + span, + }) + } + + fn analyze_func_call_expr(&mut self, func_name: String, args: Vec, span: Span) -> Option { + let func_id = match self.function_map.get(&func_name).cloned() { + Some(func_id) => func_id, + None => { + self.add_error(SemaError::FunctionNotFound(func_name), span); + return None; + } + }; + let func_def = self.functions[func_id.0].clone(); + + if args.len() < func_def.parameter_types.len() { + self.add_error(SemaError::TooFewArguments(func_def.parameter_types.len(), args.len()), span); + return None; + } + if args.len() > func_def.parameter_types.len() { + self.add_error(SemaError::TooManyArguments(func_def.parameter_types.len(), args.len()), span); + return None; + } + + let mut has_error = false; + for parameter_type in &func_def.parameter_types { + if matches!(parameter_type, SemaType::Void) { + self.add_error(SemaError::InvalidParameterType(SemaType::Void), span); + has_error = true; + } + } + if has_error { + return None; + } + + let mut hir_args = vec![]; + for (i, arg) in args.into_iter().enumerate() { + let arg = self.analyze_expr(arg)?; + let parameter_type = func_def.parameter_types[i]; + if parameter_type != arg.ty { + self.add_error(SemaError::TypeMismatch(parameter_type, arg.ty), span); + has_error = true; + continue; + } + hir_args.push(arg); + } + if has_error { + return None; + } + + Some(HirExpr { + value: HirExprValue::FuncCall(func_id, hir_args), + ty: func_def.return_type, + span, + }) + } +} diff --git a/src/sema/err.rs b/src/sema/err.rs new file mode 100644 index 0000000..13f8f3b --- /dev/null +++ b/src/sema/err.rs @@ -0,0 +1,35 @@ +use thiserror::Error; + +use crate::sema::types::SemaType; + +#[derive(Debug, Clone, PartialEq, Eq, Error)] +pub enum SemaError { + #[error("variable `{0}` not found")] + VariableNotFound(String), + #[error("variable `{0}` has already been defined")] + VariableHasBeenDefined(String), + #[error("function `{0}` not found")] + FunctionNotFound(String), + #[error("function `{0}` has already been defined")] + FunctionHasBeenDefined(String), + #[error("incompatible operands: {0} and {1}")] + IncompatiableOperand(SemaType, SemaType), + #[error("invalid operand type: {0}")] + InvalidOperand(SemaType), + #[error("too few arguments: expected {0}, got {1}")] + TooFewArguments(usize, usize), + #[error("too many arguments: expected {0}, got {1}")] + TooManyArguments(usize, usize), + #[error("type mismatch, expected {0}, got {1}")] + TypeMismatch(SemaType, SemaType), + #[error("invalid assignment target")] + InvalidAssignmentTarget, + #[error("break statement outside of loop")] + BreakOutsideLoop, + #[error("continue statement outside of loop")] + ContinueOutsideLoop, + #[error("invalid parameter type: {0}")] + InvalidParameterType(SemaType), + #[error("return expression on void function")] + ReturnExpressionOnVoidFunction, +} diff --git a/src/sema/hir.rs b/src/sema/hir.rs new file mode 100644 index 0000000..3907ebc --- /dev/null +++ b/src/sema/hir.rs @@ -0,0 +1,106 @@ +use crate::{ast::types::{BinaryOp, UnaryOp}, diagnostic::span::Span, sema::{symbol::{FunctionId, FunctionSig, SymbolId}, types::SemaType}}; + +pub struct HirCompileUnit { + pub global_decls: Vec, +} + +pub enum HirGlobalDeclStmt { + VarDecl(HirVarDeclStmt), + FuncDecl(HirFuncDeclStmt), +} + +pub struct HirVarDeclStmt { + pub values: Vec, + pub data_type: SemaType, + pub type_span: Span, +} + +pub struct HirVarDeclStmtValue { + pub symbol: SymbolId, + pub name_span: Span, +} + +pub struct HirFuncDeclStmt { + pub sig: FunctionSig, + pub params: Vec, + pub body: HirBlockStmt, + pub ret_type_span: Span, + pub name_span: Span, +} + +pub struct HirParam { + pub symbol: SymbolId, + pub param_type: SemaType, + pub name_span: Span, + pub type_span: Span, +} + +pub struct HirBlockStmt { + pub statements: Vec, +} + +pub enum HirStatement { + Return(HirReturnStmt), + Block(HirBlockStmt), + Expr(HirExpr), + VarDecl(HirVarDeclStmt), + If(HirIfStmt), + While(HirWhileStmt), + Break(HirBreakStmt), + Continue(HirContinueStmt), +} + +pub struct HirIfStmt { + pub condition: HirExpr, + pub then_branch: HirBlockStmt, + pub ifelse_branch: Vec, + pub else_branch: Option, +} + +pub struct HirIfElseBranch { + pub condition: HirExpr, + pub then_branch: HirBlockStmt, +} + +pub struct HirWhileStmt { + pub condition: HirExpr, + pub body: HirBlockStmt, +} + +pub struct HirBreakStmt { + pub span: Span, +} + +pub struct HirContinueStmt { + pub span: Span, +} + +pub struct HirReturnStmt { + pub value: Option, + pub span: Span, +} + +pub struct HirExpr { + pub value: HirExprValue, + pub ty: SemaType, + pub span: Span, +} + +pub enum HirExprValue { + IntLit(i64), + Var(SymbolId), + BinaryOp { + lhs: Box, + op: BinaryOp, + rhs: Box, + }, + UnaryOp { + op: UnaryOp, + operand: Box, + }, + FuncCall(FunctionId, Vec), + Assign { + lvalue: Box, + rvalue: Box, + }, +} diff --git a/src/sema/mod.rs b/src/sema/mod.rs new file mode 100644 index 0000000..de40432 --- /dev/null +++ b/src/sema/mod.rs @@ -0,0 +1,5 @@ +pub mod analyzer; +pub mod err; +pub mod hir; +pub mod symbol; +pub mod types; diff --git a/src/sema/symbol.rs b/src/sema/symbol.rs new file mode 100644 index 0000000..46983ca --- /dev/null +++ b/src/sema/symbol.rs @@ -0,0 +1,83 @@ +use std::collections::{BTreeMap, BTreeSet}; + +use crate::sema::{err::SemaError, types::SemaType}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct SymbolId(pub usize); + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SymbolKind { + Global, + Local, + Param, +} + +#[derive(Clone, Debug)] +pub struct Symbol { + pub id: SymbolId, + pub name: String, + pub ty: SemaType, + pub kind: SymbolKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FunctionId(pub usize); + +#[derive(Clone, Debug)] +pub struct FunctionSig { + pub id: FunctionId, + pub name: String, + pub return_type: SemaType, + pub parameter_types: Vec, +} + +pub struct SymbolTable { + symbols: Vec, + variable_map: BTreeMap>, + scopes: Vec>, +} + +impl SymbolTable { + pub fn new() -> Self { + Self { + symbols: vec![], + variable_map: BTreeMap::new(), + scopes: vec![BTreeSet::new()], + } + } + + pub fn enter_scope(&mut self) { + self.scopes.push(BTreeSet::new()); + } + + pub fn exit_scope(&mut self) { + let variables = self.scopes.pop().unwrap(); + for var in variables { + self.variable_map.get_mut(&var).unwrap().pop(); + } + } + + pub fn declare_variable(&mut self, name: &str, kind: SymbolKind, ty: SemaType) -> Result { + if self.scopes.last().unwrap().contains(name) { + return Err(SemaError::VariableHasBeenDefined(name.to_string())); + } + let id = SymbolId(self.symbols.len()); + self.symbols.push(Symbol { + id, + name: name.to_string(), + ty, + kind, + }); + self.variable_map.entry(name.to_string()).or_default().push(id); + self.scopes.last_mut().unwrap().insert(name.to_string()); + Ok(id) + } + + pub fn get_variable(&self, name: &str) -> Option { + self.variable_map.get(name).and_then(|vars| vars.last()).cloned() + } + + pub fn get_symbol(&self, id: SymbolId) -> &Symbol { + &self.symbols[id.0] + } +} diff --git a/src/sema/types.rs b/src/sema/types.rs new file mode 100644 index 0000000..1334101 --- /dev/null +++ b/src/sema/types.rs @@ -0,0 +1,51 @@ +use std::fmt::Display; + +use crate::{ast::types::Type as AstType, ir::types::IRType}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SemaType { + I32, + I1, + Void, +} + +impl SemaType { + pub fn get_elevate_result(lhs: SemaType, rhs: SemaType) -> Option { + if lhs == rhs { + Some(lhs) + } else if (lhs == SemaType::I32 && rhs == SemaType::I1) || (lhs == SemaType::I1 && rhs == SemaType::I32) { + Some(SemaType::I32) + } else { + None + } + } +} + +impl Display for SemaType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SemaType::I32 => write!(f, "i32"), + SemaType::I1 => write!(f, "i1"), + SemaType::Void => write!(f, "void"), + } + } +} + +impl From for SemaType { + fn from(value: AstType) -> Self { + match value { + AstType::Int => SemaType::I32, + AstType::Void => SemaType::Void, + } + } +} + +impl From for IRType { + fn from(value: SemaType) -> Self { + match value { + SemaType::I32 => IRType::I32, + SemaType::I1 => IRType::I1, + SemaType::Void => IRType::Void, + } + } +}