refactor(sema): Separated from ir
This commit is contained in:
+10
-7
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -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),
|
||||
}
|
||||
#[error(transparent)]
|
||||
Sema(#[from] SemaError),
|
||||
}
|
||||
|
||||
+78
-246
@@ -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<String, Function>,
|
||||
current_func_return_type: Option<IRType>,
|
||||
diagnostic: Diagnositics,
|
||||
current_exit_label: Vec<Option<(usize, usize)>>, // 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<IRInstr> {
|
||||
pub fn emit(&mut self, compile_unit: HirCompileUnit) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
|
||||
fn generate_compile_unit(&mut self, compile_unit: HirCompileUnit) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
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<Variable> = 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<IRInstr> {
|
||||
let parameters: Vec<Variable> = 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::<Vec<_>>();
|
||||
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<IRInstr> {
|
||||
fn generate_block_stmt(&mut self, block_stmt: HirBlockStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
use Statement::*;
|
||||
fn generate_statement(&mut self, stmt: HirStatement) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_return_stmt(&mut self, return_stmt: HirReturnStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_while_stmt(&mut self, while_stmt: HirWhileStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_continue_stmt(&mut self, _stmt: HirContinueStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr> {
|
||||
fn generate_break_stmt(&mut self, _stmt: HirBreakStmt) -> Vec<IRInstr> {
|
||||
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<IRInstr>, Option<Variable>)> {
|
||||
fn generate_expr(&mut self, expr: HirExpr) -> Option<(Vec<IRInstr>, Option<Variable>)> {
|
||||
// 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<String, Vec<Variable>>,
|
||||
scopes: Vec<BTreeSet<String>>,
|
||||
variable_map: BTreeMap<SymbolId, Variable>,
|
||||
global_counter: usize,
|
||||
local_counter: usize,
|
||||
local_var_type: Vec<Variable>,
|
||||
@@ -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<Variable, IRError> {
|
||||
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<Variable, IRError> {
|
||||
self.declare_variable(name, VariableType::Global, var_data_type)
|
||||
}
|
||||
pub fn declare_local(&mut self, name: &str, var_data_type: IRType) -> Result<Variable, IRError> {
|
||||
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<Variable> {
|
||||
self.local_var_type.iter().cloned().collect()
|
||||
}
|
||||
pub fn get_variable(&self, name: &str) -> Option<Variable> {
|
||||
self.variable_map.get(name).and_then(|vars| vars.last()).cloned()
|
||||
pub fn get_symbol(&self, symbol: SymbolId) -> Option<Variable> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -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::<Vec<_>>().join("\n")) {
|
||||
|
||||
@@ -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<String, FunctionId>,
|
||||
functions: Vec<FunctionSig>,
|
||||
current_func_return_type: Option<SemaType>,
|
||||
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<SemaType>, 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<HirFuncDeclStmt> {
|
||||
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::<Vec<_>>();
|
||||
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<HirStatement> {
|
||||
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<HirExpr> {
|
||||
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<HirExpr> {
|
||||
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<HirExpr> {
|
||||
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<Expr>, span: Span) -> Option<HirExpr> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
+106
@@ -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<HirGlobalDeclStmt>,
|
||||
}
|
||||
|
||||
pub enum HirGlobalDeclStmt {
|
||||
VarDecl(HirVarDeclStmt),
|
||||
FuncDecl(HirFuncDeclStmt),
|
||||
}
|
||||
|
||||
pub struct HirVarDeclStmt {
|
||||
pub values: Vec<HirVarDeclStmtValue>,
|
||||
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<HirParam>,
|
||||
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<HirStatement>,
|
||||
}
|
||||
|
||||
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<HirIfElseBranch>,
|
||||
pub else_branch: Option<HirBlockStmt>,
|
||||
}
|
||||
|
||||
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<HirExpr>,
|
||||
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<HirExpr>,
|
||||
op: BinaryOp,
|
||||
rhs: Box<HirExpr>,
|
||||
},
|
||||
UnaryOp {
|
||||
op: UnaryOp,
|
||||
operand: Box<HirExpr>,
|
||||
},
|
||||
FuncCall(FunctionId, Vec<HirExpr>),
|
||||
Assign {
|
||||
lvalue: Box<HirExpr>,
|
||||
rvalue: Box<HirExpr>,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
pub mod analyzer;
|
||||
pub mod err;
|
||||
pub mod hir;
|
||||
pub mod symbol;
|
||||
pub mod types;
|
||||
@@ -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<SemaType>,
|
||||
}
|
||||
|
||||
pub struct SymbolTable {
|
||||
symbols: Vec<Symbol>,
|
||||
variable_map: BTreeMap<String, Vec<SymbolId>>,
|
||||
scopes: Vec<BTreeSet<String>>,
|
||||
}
|
||||
|
||||
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<SymbolId, SemaError> {
|
||||
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<SymbolId> {
|
||||
self.variable_map.get(name).and_then(|vars| vars.last()).cloned()
|
||||
}
|
||||
|
||||
pub fn get_symbol(&self, id: SymbolId) -> &Symbol {
|
||||
&self.symbols[id.0]
|
||||
}
|
||||
}
|
||||
@@ -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<SemaType> {
|
||||
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<AstType> for SemaType {
|
||||
fn from(value: AstType) -> Self {
|
||||
match value {
|
||||
AstType::Int => SemaType::I32,
|
||||
AstType::Void => SemaType::Void,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SemaType> for IRType {
|
||||
fn from(value: SemaType) -> Self {
|
||||
match value {
|
||||
SemaType::I32 => IRType::I32,
|
||||
SemaType::I1 => IRType::I1,
|
||||
SemaType::Void => IRType::Void,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user