From 669c415bd7f4cf818fa95336c6f853c152a7dee0 Mon Sep 17 00:00:00 2001 From: Hydrostic Date: Sun, 31 May 2026 22:06:09 +0800 Subject: [PATCH] feat(parser,ir,backend): Support array --- src/ast/graph.rs | 28 +++++- src/ast/types.rs | 17 ++++ src/backend/generator.rs | 61 ++++++++++-- src/backend/register_allocator.rs | 12 +-- src/frontend/lexer.rs | 6 +- src/frontend/parser.rs | 127 ++++++++++++++++++------ src/frontend/types.rs | 6 +- src/ir/generator.rs | 155 +++++++++++++++++++++--------- src/ir/types.rs | 101 +++++++++++++++++-- src/sema/analyzer.rs | 132 ++++++++++++++++++++----- src/sema/err.rs | 4 + src/sema/hir.rs | 4 + src/sema/types.rs | 52 +++++++++- 13 files changed, 575 insertions(+), 130 deletions(-) diff --git a/src/ast/graph.rs b/src/ast/graph.rs index 3bebcf2..e477170 100644 --- a/src/ast/graph.rs +++ b/src/ast/graph.rs @@ -2,7 +2,7 @@ use petgraph::dot::{Config, Dot}; use petgraph::graph::{Graph, NodeIndex}; use crate::ast::types::{ - BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt + ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt }; pub type AstGraph = Graph; @@ -73,7 +73,19 @@ impl AstGraphBuilder { } fn add_var_decl_value(&mut self, parent: NodeIndex, value: &VarDeclStmtValue) -> NodeIndex { - self.child(parent, value.to_string()) + let node = self.child(parent, value.to_string()); + for dimension in &value.dimensions { + self.add_array_dimension(node, dimension); + } + node + } + + fn add_array_dimension(&mut self, parent: NodeIndex, dimension: &ArrayDimension) -> NodeIndex { + let node = self.child(parent, dimension.to_string()); + if let Some(value) = &dimension.value { + self.add_expr(node, value); + } + node } fn add_func_decl(&mut self, parent: NodeIndex, func_decl: &FuncDeclStmt) -> NodeIndex { @@ -87,7 +99,11 @@ impl AstGraphBuilder { } fn add_param(&mut self, parent: NodeIndex, param: &Param) -> NodeIndex { - self.child(parent, param.to_string()) + let node = self.child(parent, param.to_string()); + for dimension in ¶m.dimensions { + self.add_array_dimension(node, dimension); + } + node } fn add_block_stmt(&mut self, parent: NodeIndex, block_stmt: &BlockStmt) -> NodeIndex { @@ -153,6 +169,12 @@ impl AstGraphBuilder { fn add_expr(&mut self, parent: NodeIndex, expr: &Expr) -> NodeIndex { match &expr.value { ExprValue::IntLit(_) | ExprValue::Var(_) => self.child(parent, expr.value.to_string()), + ExprValue::ArrayAccess { array, index } => { + let node = self.child(parent, expr.value.to_string()); + self.add_expr(node, array); + self.add_expr(node, index); + node + } ExprValue::BinaryOp { lhs, op: _, rhs } => { let node = self.child(parent, expr.value.to_string()); self.add_expr(node, lhs); diff --git a/src/ast/types.rs b/src/ast/types.rs index 15c19bb..b9867ce 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -17,7 +17,12 @@ pub struct VarDeclStmt { pub struct VarDeclStmtValue { pub name: String, pub name_span: Span, + pub dimensions: Vec, +} +pub struct ArrayDimension { + pub value: Option, + pub span: Span, } pub struct FuncDeclStmt { @@ -89,6 +94,10 @@ pub struct Expr { pub enum ExprValue { IntLit(i64), Var(String), + ArrayAccess { + array: Box, + index: Box, + }, BinaryOp { lhs: Box, op: BinaryOp, @@ -159,6 +168,7 @@ impl From for Type { pub struct Param { pub name: String, pub param_type: Type, + pub dimensions: Vec, pub name_span: Span, pub type_span: Span, } @@ -190,6 +200,12 @@ impl fmt::Display for VarDeclStmtValue { } } +impl fmt::Display for ArrayDimension { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ArrayDimension") + } +} + impl fmt::Display for FuncDeclStmt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} {}", self.return_type, self.name) @@ -256,6 +272,7 @@ impl fmt::Display for ExprValue { match self { ExprValue::IntLit(value) => write!(f, "IntLit({})", value), ExprValue::Var(name) => write!(f, "Var({})", name), + ExprValue::ArrayAccess { .. } => write!(f, "ArrayAccess"), ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op), ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name), ExprValue::Assign { .. } => write!(f, "Assign"), diff --git a/src/backend/generator.rs b/src/backend/generator.rs index 7c31c31..3457a96 100644 --- a/src/backend/generator.rs +++ b/src/backend/generator.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}}; +use crate::{backend::{arm_instr::{ARMInstr, AddInstr, BInstr, BlInstr, CmpInstr, ConditionCode, LoadInstr, LoadPseudoInstr, MoveInstr, MulInstr, PopInstr, PushInstr, RegisterOrImm, RsbInstr, SDivInstr, StoreInstr, SubInstr}, register_allocator::{REG_FP, REG_R0, REG_R1, REG_R2, REG_R3, Register, RegisterAlloc, RegisterAllocator}, types::ARMAsmVar}, ir::types::{Function, IRInstr, MoveRValue, Variable, VariableOrIntLit, VariableType}}; use crate::ir::types::BinaryOp as IRBinaryOp; use crate::ir::types::CmpOp as IRCmpOp; use crate::ir::types::UnaryOp as IRUnaryOp; @@ -19,7 +19,7 @@ const ARG_REGS: [Register; 4] = [REG_R0, REG_R1, REG_R2, REG_R3]; fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap, instrs: &mut Vec) -> RegisterAlloc { match variable.var_type { VariableType::Global => { - let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers"); + let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); let var_reg = var_alloc.reg; // if !var_alloc.is_reused { let address_alloc = reg_allocator.alloc_any().expect("Ran out of registers"); @@ -39,7 +39,7 @@ fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_ }, _ => { let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared"); - let var_alloc = reg_allocator.alloc(variable).expect("Ran out of registers"); + let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); // if !var_alloc.is_reused { instrs.push(LoadInstr::new_stack(var_alloc.reg, *stack_offset as i32)); // } @@ -134,6 +134,10 @@ impl Generator { }, IRInstr::FuncCall(func, args, ret) => self.emit_func_call(func, args, ret, &var_index_to_stack_offset), IRInstr::Move(dest, src) => self.emit_move(dest, src, &var_index_to_stack_offset), + IRInstr::Load(dest, addr) => self.emit_load(dest, addr, &var_index_to_stack_offset), + IRInstr::Store(addr, value) => self.emit_store(addr, value, &var_index_to_stack_offset), + IRInstr::GetAddr(dest, var) => self.emit_get_addr(dest, var, &var_index_to_stack_offset), + IRInstr::GetElementPtr(dest, base, index, elem_size) => self.emit_get_element_ptr(dest, base, index, elem_size, &var_index_to_stack_offset), IRInstr::Declare(variable) => { assert!(!encounter_entry, "Variable declarations must come before entry instruction"); let size = variable.data_type.size_in_bytes(); @@ -163,7 +167,7 @@ impl Generator { } fn emit_unary(&mut self, dest: Variable, unary_op: IRUnaryOp, variable: Variable, var_index_to_stack_offset: &BTreeMap) { let variable_alloc = load_variable(variable, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); - let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers"); + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); match unary_op { IRUnaryOp::Neg => { self.instrs.push(RsbInstr::new(dest_alloc.reg, variable_alloc.reg, RegisterOrImm::Imm(0))); @@ -193,7 +197,7 @@ impl Generator { alloc }, }; - let variable_alloc = self.register_allocator.alloc(variable).expect("Ran out of registers"); + let variable_alloc = self.register_allocator.alloc(variable.clone()).expect("Ran out of registers"); let variable_reg = variable_alloc.reg; self.instrs.push(CmpInstr::new(left_alloc.reg, RegisterOrImm::Reg(right_alloc.reg))); self.instrs.push(MoveInstr::new_uncond(variable_reg, RegisterOrImm::Imm(0))); @@ -221,7 +225,7 @@ impl Generator { self.instrs.push(PopInstr::new_pop_caller_save()); } fn emit_move(&mut self, dest: Variable, src: MoveRValue, var_index_to_stack_offset: &BTreeMap) { - let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers"); + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); let dest_register = dest_alloc.reg; match src { MoveRValue::Var(variable) => { @@ -232,11 +236,50 @@ impl Generator { }; save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); } + + fn emit_get_addr(&mut self, dest: Variable, var: Variable, var_index_to_stack_offset: &BTreeMap) { + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); + match var.var_type { + VariableType::Global => { + self.instrs.push(LoadPseudoInstr::new(dest_alloc.reg, format!("global_var_{}", var.index))); + } + _ => { + let stack_offset = var_index_to_stack_offset.get(&var.index).expect("Variable not declared"); + self.instrs.push(SubInstr::new(dest_alloc.reg, REG_FP, RegisterOrImm::Imm(*stack_offset as i32))); + } + } + save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + } + + fn emit_get_element_ptr(&mut self, dest: Variable, base: Variable, index: Variable, elem_size: usize, var_index_to_stack_offset: &BTreeMap) { + let base_alloc = load_variable(base, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + let index_alloc = load_variable(index, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + let scaled_alloc = self.register_allocator.alloc_any().expect("Ran out of registers"); + self.instrs.push(MoveInstr::new_uncond(scaled_alloc.reg, RegisterOrImm::Imm(elem_size as i32))); + self.instrs.push(MulInstr::new(scaled_alloc.reg, index_alloc.reg, RegisterOrImm::Reg(scaled_alloc.reg))); + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); + self.instrs.push(AddInstr::new(dest_alloc.reg, base_alloc.reg, RegisterOrImm::Reg(scaled_alloc.reg))); + save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + } + + fn emit_load(&mut self, dest: Variable, addr: Variable, var_index_to_stack_offset: &BTreeMap) { + let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); + self.instrs.push(LoadInstr::new(dest_alloc.reg, addr_alloc.reg, None)); + save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + } + + fn emit_store(&mut self, addr: Variable, value: Variable, var_index_to_stack_offset: &BTreeMap) { + let addr_alloc = load_variable(addr, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + let value_alloc = load_variable(value, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + self.instrs.push(StoreInstr::new(value_alloc.reg, addr_alloc.reg, None)); + } + fn emit_binary(&mut self, dest: Variable, left: Variable, op: IRBinaryOp, right: Variable, var_index_to_stack_offset: &BTreeMap) { - let dest_alloc = self.register_allocator.alloc(dest).expect("Ran out of registers"); + let dest_alloc = self.register_allocator.alloc(dest.clone()).expect("Ran out of registers"); let dest_reg = dest_alloc.reg; // should consider left == right - let left_alloc = load_variable(left, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); + let left_alloc = load_variable(left.clone(), &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let (_right_alloc, right_reg) = if left != right { let right_alloc = load_variable(right, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); let right_reg = right_alloc.reg; @@ -268,4 +311,4 @@ impl Generator { save_variable(dest, dest_alloc.reg, &mut self.register_allocator, var_index_to_stack_offset, &mut self.instrs); } -} \ No newline at end of file +} diff --git a/src/backend/register_allocator.rs b/src/backend/register_allocator.rs index 795df33..54b22af 100644 --- a/src/backend/register_allocator.rs +++ b/src/backend/register_allocator.rs @@ -84,7 +84,7 @@ impl RegisterAllocatorInner { *use_kind = RegisterUseKind::Free; }, RegisterUseKind::UsedByVariable(var) => { - *use_kind = RegisterUseKind::AllocatedToVariable(*var); + *use_kind = RegisterUseKind::AllocatedToVariable(var.clone()); }, _ => panic!("Trying to mark a register as unused that is not in use"), } @@ -139,8 +139,8 @@ impl RegisterAllocator { continue; } if let RegisterUseKind::Free = use_kind { - *use_kind = RegisterUseKind::UsedByVariable(var); - inner.variable_to_register.insert(var, reg); + *use_kind = RegisterUseKind::UsedByVariable(var.clone()); + inner.variable_to_register.insert(var.clone(), reg); return Some(RegisterAlloc { allocator: Rc::downgrade(&self.inner), reg, @@ -156,8 +156,8 @@ impl RegisterAllocator { } if let RegisterUseKind::AllocatedToVariable(ori_var) = use_kind { assert!(variable_to_register.remove(&ori_var).is_some()); - *use_kind = RegisterUseKind::UsedByVariable(var); - variable_to_register.insert(var, reg); + *use_kind = RegisterUseKind::UsedByVariable(var.clone()); + variable_to_register.insert(var.clone(), reg); return Some(RegisterAlloc { allocator: Rc::downgrade(&self.inner), reg, @@ -245,4 +245,4 @@ impl RegisterAllocator { self.debug_use(); None } -} \ No newline at end of file +} diff --git a/src/frontend/lexer.rs b/src/frontend/lexer.rs index 7c9d01c..274f5ef 100644 --- a/src/frontend/lexer.rs +++ b/src/frontend/lexer.rs @@ -17,7 +17,7 @@ pub struct Lexer { const WHITESPACE_CHARS: &[char] = &[' ', '\t', '\n', '\r']; const DELIMITER_CHARS: &[char] = &[ - '+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', ',', ';', '{', '|', '&' + '+', '-', '*', '/', '%', '=', '!', '<', '>', '(', ')', '[', ']', ',', ';', '{', '|', '&' ]; struct Cursor { chars: Vec, @@ -248,6 +248,8 @@ fn parse_delimiter( ')' => TokenValue::RParen, '{' => TokenValue::LBrace, '}' => TokenValue::RBrace, + '[' => TokenValue::LBracket, + ']' => TokenValue::RBracket, _ => return Err(LexParseError::NotMatched), }; str_iter.advance(1); @@ -398,4 +400,4 @@ mod tests { fn test_expr() { test_case("0-3,14-25"); } -} \ No newline at end of file +} diff --git a/src/frontend/parser.rs b/src/frontend/parser.rs index 1f8972d..13ee10a 100644 --- a/src/frontend/parser.rs +++ b/src/frontend/parser.rs @@ -1,6 +1,6 @@ use crate::{ ast::types::{ - BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, Param, ReturnStmt, Statement, UnaryOp, VarDeclStmt, VarDeclStmtValue, WhileStmt + ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, Param, ReturnStmt, Statement, UnaryOp, VarDeclStmt, VarDeclStmtValue, WhileStmt }, diagnostic::{Diagnositics, span::Span}, frontend::{ @@ -259,6 +259,7 @@ impl Parser { Ok(Param { param_type: param_type.into(), name, + dimensions: self.parse_array_dimensions(true)?, name_span, type_span, }) @@ -300,7 +301,8 @@ impl Parser { } } }; - values.push(VarDeclStmtValue { name, name_span }); + let dimensions = self.parse_array_dimensions(false)?; + values.push(VarDeclStmtValue { name, name_span, dimensions }); let mut last_name = true; // indicate whether the last parsed token is a variable name while let Some(t) = self.peek() { if matches!(t.value, TokenValue::Semicolon) { // statement end @@ -319,7 +321,8 @@ impl Parser { } if let Some(ident) = self.peek().unwrap().value.as_ident() { let span = self.next().unwrap().span; - values.push(VarDeclStmtValue { name: ident, name_span: span }); + let dimensions = self.parse_array_dimensions(false)?; + values.push(VarDeclStmtValue { name: ident, name_span: span, dimensions }); last_name = true; } else { let token = self.next().unwrap().clone(); @@ -334,6 +337,50 @@ impl Parser { Ok(VarDeclStmt { values, type_span, data_type: var_type.into() }) } + fn parse_array_dimensions(&mut self, allow_empty_first: bool) -> Result, ParseProcessError> { + let mut dimensions = vec![]; + while self.peek().is_some_and(|t| t.value == TokenValue::LBracket) { + let start_span = self.next().unwrap().span; + let value = if self.peek().is_some_and(|t| t.value == TokenValue::RBracket) { + if !allow_empty_first || !dimensions.is_empty() { + let span = self.peek().unwrap().span; + self.diagnostics.add_from_frontend_error( + ParseError::UnexpectedToken(TokenValue::RBracket, "array dimension expression"), + span, + ); + return Err(ParseProcessError::ErrorInMatch); + } + None + } else { + Some(self.parse_expr()?) + }; + let end_span = match self.peek() { + Some(t) if t.value == TokenValue::RBracket => { + let span = t.span; + self.advance(1); + span + } + Some(_) => { + let token = self.next().unwrap().clone(); + self.diagnostics.add_from_frontend_error( + ParseError::UnexpectedToken(token.value, "`]`"), + token.span, + ); + return Err(ParseProcessError::ErrorInMatch); + } + None => { + self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), start_span); + return Err(ParseProcessError::ErrorInMatch); + } + }; + dimensions.push(ArrayDimension { + value, + span: Span::from_two(start_span, end_span), + }); + } + Ok(dimensions) + } + fn parse_block_stmt(&mut self, parse_type: ParseType) -> Result { assert!(self.peek().is_some()); if self @@ -598,7 +645,7 @@ impl Parser { // } // } // } - fn parse_primary(&mut self) -> Result { + fn parse_primary_atom(&mut self) -> Result { assert!(self.peek().is_some()); let token = self.next().unwrap().clone(); match token.value { @@ -712,6 +759,47 @@ impl Parser { } } } + fn parse_primary(&mut self) -> Result { + let mut expr = self.parse_primary_atom()?; + while self.peek().is_some_and(|t| t.value == TokenValue::LBracket) { + let start_span = expr.span; + self.advance(1); + let index = match self.peek() { + Some(_) => self.parse_expr()?, + None => { + self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("array index expression"), start_span); + return Err(ParseProcessError::ErrorInMatch); + } + }; + let end_span = match self.peek() { + Some(t) if t.value == TokenValue::RBracket => { + let span = t.span; + self.advance(1); + span + } + Some(_) => { + let token = self.next().unwrap().clone(); + self.diagnostics.add_from_frontend_error( + ParseError::UnexpectedToken(token.value, "`]`"), + token.span, + ); + return Err(ParseProcessError::ErrorInMatch); + } + None => { + self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), index.span); + return Err(ParseProcessError::ErrorInMatch); + } + }; + expr = Expr { + value: ExprValue::ArrayAccess { + array: Box::new(expr), + index: Box::new(index), + }, + span: Span::from_two(start_span, end_span), + }; + } + Ok(expr) + } fn parse_unary(&mut self) -> Result { assert!(self.peek().is_some()); let token = self.peek().unwrap().clone(); @@ -937,40 +1025,21 @@ impl Parser { } fn parse_assign(&mut self) -> Result { assert!(self.peek().is_some()); - let is_assign = matches!( - (self.tokens.get(self.pos), self.tokens.get(self.pos + 1)), - ( - Some(Token { - value: TokenValue::Ident(_), - .. - }), - Some(Token { - value: TokenValue::Equal, - .. - }) - ) - ); - if !is_assign { - return self.parse_logical_or(); + let lvalue = self.parse_logical_or()?; + if !self.peek().is_some_and(|t| t.value == TokenValue::Equal) { + return Ok(lvalue); } - - let lvalue_token = self.next().unwrap().clone(); - let name = lvalue_token.value.as_ident().unwrap(); self.advance(1); let rvalue = match self.peek() { Some(_) => self.parse_assign()?, None => { self.diagnostics.add_from_frontend_error( ParseError::ExpectButEof("expression"), - lvalue_token.span, + lvalue.span, ); return Err(ParseProcessError::ErrorInMatch); } }; - let lvalue = Expr { - value: ExprValue::Var(name), - span: lvalue_token.span, - }; let span = Span::from_two(lvalue.span, rvalue.span); Ok(Expr { value: ExprValue::Assign { @@ -1100,4 +1169,8 @@ mod tests { fn test_func() { test_case("12-13,58-60"); } + #[test] + fn test_array() { + test_case("4-7,11,42,71,74-78,82,84,86,90,95,96,100,101,103"); + } } diff --git a/src/frontend/types.rs b/src/frontend/types.rs index 1ad2ea8..dacef8a 100644 --- a/src/frontend/types.rs +++ b/src/frontend/types.rs @@ -21,6 +21,7 @@ pub enum TokenValue { LParen, RParen, LBrace, RBrace, + LBracket, RBracket, Comma, Semicolon, If, Else, While, Return, Break, Continue, @@ -69,6 +70,8 @@ impl std::fmt::Display for TokenValue { TokenValue::RParen => write!(f, "`)`"), TokenValue::LBrace => write!(f, "`{{`"), TokenValue::RBrace => write!(f, "`}}`"), + TokenValue::LBracket => write!(f, "`[`"), + TokenValue::RBracket => write!(f, "`]`"), TokenValue::Comma => write!(f, "`,`"), TokenValue::Semicolon => write!(f, "`;`"), TokenValue::If => write!(f, "if"), @@ -94,6 +97,7 @@ pub enum TokenKind { LParen, RParen, LBrace, RBrace, + LBracket, RBracket, Comma, Semicolon, If, Else, While, Return, Break, Continue, @@ -107,4 +111,4 @@ pub enum TypeIdent { Int, #[strum(serialize = "void")] Void, -} \ No newline at end of file +} diff --git a/src/ir/generator.rs b/src/ir/generator.rs index 2ea9ebf..d5eee51 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, vec}; -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::{ir::types::{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<'a> { @@ -41,8 +41,8 @@ impl<'a> Generator<'a> { 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(), + parameter_types: sig.parameter_types.iter().map(|ty| ty.clone().into()).collect(), + return_type: sig.return_type.clone().into(), } } @@ -66,7 +66,7 @@ impl<'a> Generator<'a> { let mut instrs = vec![]; let var_type = if is_global { VariableType::Global } else { VariableType::Local }; for value in var_decl.values { - let var = self.var_manager.declare_symbol(value.symbol, var_type, var_decl.data_type.into()); + let var = self.var_manager.declare_symbol(value.symbol, var_type, self.sema.get_symbol_type(value.symbol).into()); if is_global { instrs.push(IRInstr::Declare(var)); } @@ -77,10 +77,10 @@ impl<'a> Generator<'a> { 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())) + .map(|param| self.var_manager.declare_symbol(param.symbol, VariableType::Local, param.param_type.clone().into())) .collect(); let temp_parameters = parameters.iter().enumerate() - .map(|(i, param)| self.var_manager.declare_param_temp(param.data_type, i)) + .map(|(i, param)| self.var_manager.declare_param_temp(param.data_type.clone(), i)) .collect::>(); let mut body_instrs = vec![]; self.func_exit = Some((self.request_label(), { @@ -100,7 +100,7 @@ impl<'a> Generator<'a> { } body_instrs.push(IRInstr::Entry); parameters.iter().zip(temp_parameters.iter()).for_each(|(param, temp_param)| { - body_instrs.push(IRInstr::Move(*param, MoveRValue::Var(*temp_param))); + body_instrs.push(IRInstr::Move(param.clone(), MoveRValue::Var(temp_param.clone()))); }); body_instrs.extend(block_instrs); let func_exit = self.func_exit.take().unwrap(); @@ -108,7 +108,7 @@ impl<'a> Generator<'a> { body_instrs.push(IRInstr::Exit(func_exit.1)); self.var_manager.clear_local_counter(); let mut func = self.ir_function(func_decl.sig.id); - func.parameter_types = temp_parameters.iter().map(|v| v.data_type).collect(); + func.parameter_types = temp_parameters.iter().map(|v| v.data_type.clone()).collect(); vec![IRInstr::DefineFunc(func, temp_parameters, body_instrs)] } fn generate_block_stmt(&mut self, block_stmt: HirBlockStmt) -> Vec { @@ -149,7 +149,7 @@ impl<'a> Generator<'a> { fn generate_return_stmt(&mut self, return_stmt: HirReturnStmt) -> Vec { let mut instrs = vec![]; - let func_exit = self.func_exit.unwrap(); + let func_exit = self.func_exit.clone().unwrap(); match return_stmt.value { Some(expr) => { self.current_exit_label.push(None); @@ -269,26 +269,49 @@ impl<'a> Generator<'a> { 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)) + (vec![IRInstr::Move(var.clone(), MoveRValue::ConstInt(i as i32))], Some(var)) }, HirExprValue::Var(symbol) => { let var = self.var_manager.get_symbol(symbol).unwrap(); - (vec![], Some(var)) + if var.data_type.is_array() { + let ptr_ty = var.data_type.decay_to_ptr().unwrap(); + let dest = self.var_manager.declare_temp(ptr_ty); + (vec![IRInstr::GetAddr(dest.clone(), var)], Some(dest)) + } else { + (vec![], Some(var)) + } }, HirExprValue::Assign { lvalue, rvalue } => { - let var = if let HirExprValue::Var(symbol) = lvalue.value { - self.var_manager.get_symbol(symbol).unwrap() - } else { - return None; - }; + let lvalue_ty: IRType = lvalue.ty.clone().into(); 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))); + let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?; + instrs.extend(lvalue_instrs); + if is_addr { + instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap())); + } else { + instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap()))); + } + let temp_var = self.var_manager.declare_temp(lvalue_ty); + if is_addr { + instrs.push(IRInstr::Load(temp_var.clone(), target)); + } else { + instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(target))); + } (instrs, Some(temp_var)) }, + HirExprValue::ArrayAccess { array, index } => { + let result_ty: IRType = expr.ty.clone().into(); + let (mut instrs, addr) = self.generate_array_element_addr(*array, *index)?; + if result_ty.is_array() { + (instrs, Some(addr)) + } else { + let dest = self.var_manager.declare_temp(result_ty); + instrs.push(IRInstr::Load(dest.clone(), addr)); + (instrs, Some(dest)) + } + }, HirExprValue::UnaryOp { op, operand } => { let mut parent_is_logical = true; let exit_passdown = if matches!(op, AstUnaryOp::Not) { @@ -319,26 +342,26 @@ impl<'a> Generator<'a> { let operand_var = operand_var.unwrap(); let dest_var = match op { AstUnaryOp::Add => { - let dest_var = self.var_manager.declare_temp(operand_var.data_type); - instrs.push(IRInstr::Move(dest_var, MoveRValue::Var(operand_var))); + let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone()); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::Var(operand_var))); dest_var }, AstUnaryOp::Sub => { - let dest_var = self.var_manager.declare_temp(operand_var.data_type); - instrs.push(IRInstr::Unary(dest_var, UnaryOp::Neg, operand_var)); + let dest_var = self.var_manager.declare_temp(operand_var.data_type.clone()); + instrs.push(IRInstr::Unary(dest_var.clone(), UnaryOp::Neg, operand_var)); dest_var }, AstUnaryOp::Not => { - let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type); + let dest_var = self.var_manager.declare_unamed_local(operand_var.data_type.clone()); // child will do the cmp if !parent_is_logical { let exit = exit_passdown.unwrap(); // (false_exit, true_exit) (consider `not`) let final_exit = self.request_label(); instrs.push(IRInstr::Label(exit.1)); - instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1))); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1))); instrs.push(IRInstr::Goto(final_exit)); instrs.push(IRInstr::Label(exit.0)); - instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0))); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0))); instrs.push(IRInstr::Goto(final_exit)); instrs.push(IRInstr::Label(final_exit)); @@ -400,21 +423,21 @@ impl<'a> Generator<'a> { let convert_to; if op.is_logical() { // we dont really care since we use cmp - convert_to = left_var.data_type; + convert_to = left_var.data_type.clone(); } else { - convert_to = IRType::get_elevate_result(left_var.data_type, right_var.data_type).unwrap(); + convert_to = IRType::get_elevate_result(&left_var.data_type, &right_var.data_type).unwrap(); } // do implicit convert if needed // TODO: further check if !op.is_logical() && convert_to != left_var.data_type { - let temp_var = self.var_manager.declare_temp(convert_to); - instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(left_var))); + let temp_var = self.var_manager.declare_temp(convert_to.clone()); + instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(left_var))); left_var = temp_var; } let result_type; match op { AstBinaryOp::Add | AstBinaryOp::Sub | AstBinaryOp::Mul | AstBinaryOp::Div | AstBinaryOp::Mod => { - result_type = left_var.data_type; + result_type = left_var.data_type.clone(); }, AstBinaryOp::Equal | AstBinaryOp::NotEqual | AstBinaryOp::Less | AstBinaryOp::Greater | AstBinaryOp::LessEqual | AstBinaryOp::GreaterEqual | AstBinaryOp::And | AstBinaryOp::Or => { @@ -448,17 +471,17 @@ impl<'a> Generator<'a> { instrs.extend(right_instrs); if !op.is_logical() && convert_to != right_var.data_type { let temp_var = self.var_manager.declare_temp(convert_to); - instrs.push(IRInstr::Move(temp_var, MoveRValue::Var(right_var))); + instrs.push(IRInstr::Move(temp_var.clone(), MoveRValue::Var(right_var))); right_var = temp_var; } if !op.is_logical() { if let Some((true_exit, false_exit)) = parent_exit { let final_exit = self.request_label(); instrs.push(IRInstr::Label(true_exit)); - instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(1))); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(1))); instrs.push(IRInstr::Goto(final_exit)); instrs.push(IRInstr::Label(false_exit)); - instrs.push(IRInstr::Move(dest_var, MoveRValue::ConstInt(0))); + instrs.push(IRInstr::Move(dest_var.clone(), MoveRValue::ConstInt(0))); instrs.push(IRInstr::Label(final_exit)); @@ -468,9 +491,9 @@ impl<'a> Generator<'a> { return Some((instrs, Some(dest_var))); } else { if op.is_cmp() { - instrs.push(IRInstr::Cmp(dest_var, VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var))); + instrs.push(IRInstr::Cmp(dest_var.clone(), VariableOrIntLit::Var(left_var), op.into(), VariableOrIntLit::Var(right_var))); } else { - instrs.push(IRInstr::Binary(dest_var, left_var, op.into(), right_var)); + instrs.push(IRInstr::Binary(dest_var.clone(), left_var, op.into(), right_var)); } } // if !op.is_logical() { @@ -504,21 +527,61 @@ impl<'a> Generator<'a> { let ret_variable = if matches!(func_def.return_type, IRType::Void) { None } else { - Some(self.var_manager.declare_temp(func_def.return_type)) + Some(self.var_manager.declare_temp(func_def.return_type.clone())) }; - instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable)); + instrs.push(IRInstr::FuncCall(func_def.clone(), arg_vars, ret_variable.clone())); (instrs, ret_variable) } }; if let Some((true_exit, false_exit)) = self.current_exit_label.last().cloned().flatten() { let cmp_var = self.var_manager.declare_temp(IRType::I1); - instrs.push(IRInstr::Cmp(cmp_var, VariableOrIntLit::Var(var.unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0))); + instrs.push(IRInstr::Cmp(cmp_var.clone(), VariableOrIntLit::Var(var.clone().unwrap()), CmpOp::Ne, VariableOrIntLit::IntLit(0))); instrs.push(IRInstr::CondGoto(cmp_var, true_exit, false_exit)); Some((instrs, var)) } else { Some((instrs, var)) } } + + fn generate_lvalue(&mut self, expr: HirExpr) -> Option<(Vec, Variable, bool)> { + match expr.value { + HirExprValue::Var(symbol) => { + let var = self.var_manager.get_symbol(symbol).unwrap(); + Some((vec![], var, false)) + } + HirExprValue::ArrayAccess { array, index } => { + let (instrs, addr) = self.generate_array_element_addr(*array, *index)?; + Some((instrs, addr, true)) + } + _ => None, + } + } + + fn generate_array_element_addr(&mut self, array: HirExpr, index: HirExpr) -> Option<(Vec, Variable)> { + let elem_ty = array.ty.indexed_type().unwrap(); + let elem_size = elem_ty.element_size_in_bytes(); + let (mut instrs, base) = match array.value { + HirExprValue::Var(symbol) => { + let var = self.var_manager.get_symbol(symbol).unwrap(); + if var.data_type.is_array() { + let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap()); + (vec![IRInstr::GetAddr(ptr.clone(), var)], ptr) + } else { + (vec![], var) + } + } + HirExprValue::ArrayAccess { array, index } => self.generate_array_element_addr(*array, *index)?, + _ => self.generate_expr(array).map(|(instrs, var)| (instrs, var.unwrap()))?, + }; + self.current_exit_label.push(None); + let (index_instrs, index_var) = self.generate_expr(index)?; + self.current_exit_label.pop(); + instrs.extend(index_instrs); + let addr_ty = IRType::Ptr(Box::new(elem_ty.into())); + let dest = self.var_manager.declare_temp(addr_ty); + instrs.push(IRInstr::GetElementPtr(dest.clone(), base, index_var.unwrap(), elem_size)); + Some((instrs, dest)) + } } struct VariableManager { @@ -551,28 +614,28 @@ impl VariableManager { } _ => unreachable!(), }; - self.variable_map.insert(symbol, variable); + self.variable_map.insert(symbol, variable.clone()); if matches!(var_type, VariableType::Local) { - self.local_var_type.push(variable); + self.local_var_type.push(variable.clone()); } 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 }; self.local_counter += 1; - self.local_var_type.push(var); + self.local_var_type.push(var.clone()); var } pub fn declare_unamed_local(&mut self, var_data_type: IRType) -> Variable { let var = Variable { index: self.local_counter, var_type: VariableType::Local, data_type: var_data_type }; self.local_counter += 1; - self.local_var_type.push(var); + self.local_var_type.push(var.clone()); var } pub fn declare_param_temp(&mut self, var_data_type: IRType, param_index: usize) -> Variable { let var = Variable { index: self.local_counter, var_type: VariableType::ParamTemp(param_index), data_type: var_data_type }; self.local_counter += 1; - self.local_var_type.push(var); + self.local_var_type.push(var.clone()); var } pub fn clear_local_counter(&mut self) { @@ -670,4 +733,8 @@ mod tests { fn test_func() { test_case("12-13,58-60"); } + #[test] + fn test_array() { + test_case("4-7,11,42,71,74-78,82,84,86,90,95,96,100,101,103"); + } } diff --git a/src/ir/types.rs b/src/ir/types.rs index 3af7c1e..4f9e382 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -30,6 +30,10 @@ pub enum IRInstr { CondGoto(Variable, usize, usize), // condition, true label, false label Label(usize), Move(Variable, MoveRValue), + Load(Variable, Variable), + Store(Variable, Variable), + GetAddr(Variable, Variable), + GetElementPtr(Variable, Variable, Variable, usize), } impl Display for IRInstr { @@ -47,7 +51,11 @@ impl Display for IRInstr { } } IRInstr::Move(dest, src) => write!(f, "{} = {}", dest, src), - IRInstr::Declare(var) => write!(f, "declare {} {}", var.data_type, var), + IRInstr::Load(dest, addr) => write!(f, "{} = *{}", dest, addr), + IRInstr::Store(addr, value) => write!(f, "*{} = {}", addr, value), + IRInstr::GetAddr(dest, var) => write!(f, "{} = getaddr {}", dest, var), + IRInstr::GetElementPtr(dest, base, index, elem_size) => write!(f, "{} = gep {}, {}, {}", dest, base, index, elem_size), + IRInstr::Declare(var) => write!(f, "declare {}", var.to_decl_string()), IRInstr::DefineFunc(func, args, body) => { let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::>().join("\n"); write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str) @@ -59,11 +67,13 @@ impl Display for IRInstr { } } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum IRType { I32, I1, Void, + Ptr(Box), + Array(Box, Vec), } impl IRType { pub fn size_in_bytes(&self) -> usize { @@ -71,13 +81,47 @@ impl IRType { IRType::I32 => 4, IRType::I1 => 1, IRType::Void => 0, + IRType::Ptr(_) => 4, + IRType::Array(elem, dims) => elem.size_in_bytes() * dims.iter().product::(), } } - pub fn get_elevate_result(lhs: IRType, rhs: IRType) -> Option { + pub fn is_array(&self) -> bool { + matches!(self, IRType::Array(_, _)) + } + + pub fn decay_to_ptr(&self) -> Option { + match self { + IRType::Array(elem, dims) => { + let pointee = if dims.len() == 1 { + (**elem).clone() + } else { + IRType::Array(elem.clone(), dims[1..].to_vec()) + }; + Some(IRType::Ptr(Box::new(pointee))) + } + _ => None, + } + } + + pub fn element_type(&self) -> Option { + match self { + IRType::Array(elem, dims) => { + if dims.len() == 1 { + Some((**elem).clone()) + } else { + Some(IRType::Array(elem.clone(), dims[1..].to_vec())) + } + } + IRType::Ptr(elem) => Some((**elem).clone()), + _ => None, + } + } + + pub fn get_elevate_result(lhs: &IRType, rhs: &IRType) -> Option { if lhs == rhs { - Some(lhs) - } else if (lhs == IRType::I32 && rhs == IRType::I1) || (lhs == IRType::I1 && rhs == IRType::I32) { + Some(lhs.clone()) + } else if (*lhs == IRType::I32 && *rhs == IRType::I1) || (*lhs == IRType::I1 && *rhs == IRType::I32) { Some(IRType::I32) } else { None @@ -90,6 +134,30 @@ impl Display for IRType { IRType::I32 => write!(f, "i32"), IRType::Void => write!(f, "void"), IRType::I1 => write!(f, "i1"), + IRType::Ptr(elem) => write!(f, "{}*", elem), + IRType::Array(elem, dims) => { + write!(f, "{}", elem)?; + for dim in dims { + write!(f, "[{}]", dim)?; + } + Ok(()) + } + } + } +} +impl IRType { + pub fn base_type_and_dims(&self) -> (&IRType, &[usize]) { + match self { + IRType::Array(elem, dims) => (elem.as_ref(), dims.as_slice()), + _ => (self, &[]), + } + } + + pub fn scalar_base_type(&self) -> &IRType { + match self { + IRType::Array(elem, _) => elem.scalar_base_type(), + IRType::Ptr(elem) => elem.scalar_base_type(), + _ => self, } } } @@ -120,7 +188,7 @@ pub enum VariableType { Local, Temp, } -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Variable { // pub name: String, pub index: usize, @@ -160,6 +228,19 @@ impl Display for Variable { write!(f, "{}{}", prefix, self.index) } } +impl Variable { + pub fn to_decl_string(&self) -> String { + match &self.data_type { + IRType::Array(_, _) => { + let (base_type, dims) = self.data_type.base_type_and_dims(); + let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::>().join(""); + format!("{} {}{}", base_type, self, dims_str) + } + IRType::Ptr(_) => format!("{}* {}", self.data_type.scalar_base_type(), self), + _ => format!("{} {}", self.data_type, self), + } + } +} #[derive(Debug, Clone)] pub struct Function { pub name: String, @@ -175,7 +256,11 @@ impl Function { } pub fn to_decl_string(&self, args: &Vec) -> String { - let params_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param_type)| format!("{} {}", param_type, arg)).collect::>().join(", "); + let params_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param_type)| { + let (base_type, dims) = param_type.base_type_and_dims(); + let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::>().join(""); + format!("{} {}{}", base_type, arg, dims_str) + }).collect::>().join(", "); format!("{} @{}({})", self.return_type, self.name, params_str) } } @@ -269,4 +354,4 @@ impl From for CmpOp { _ => unreachable!(), } } -} \ No newline at end of file +} diff --git a/src/sema/analyzer.rs b/src/sema/analyzer.rs index c60acec..fbd0c34 100644 --- a/src/sema/analyzer.rs +++ b/src/sema/analyzer.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use crate::{ ast::types::{ - BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, + ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt, }, diagnostic::{span::Span, Diagnositics}, @@ -38,6 +38,7 @@ impl Analyzer { loop_depth: 0, }; analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void); + analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void); analyzer.declare_builtin_func("getint", vec![], SemaType::I32); analyzer } @@ -51,7 +52,7 @@ impl Analyzer { } pub fn get_symbol_type(&self, symbol: SymbolId) -> SemaType { - self.symbols.get_symbol(symbol).ty + self.symbols.get_symbol(symbol).ty.clone() } pub fn get_symbol_kind(&self, symbol: SymbolId) -> SymbolKind { @@ -96,9 +97,10 @@ impl Analyzer { } fn analyze_var_decl(&mut self, var_decl: VarDeclStmt, kind: SymbolKind) -> HirVarDeclStmt { - let data_type = var_decl.data_type.into(); + let base_type: SemaType = var_decl.data_type.into(); let mut values = vec![]; for value in var_decl.values { + let data_type = self.build_var_type(base_type.clone(), &value.dimensions, false); match self.symbols.declare_variable(&value.name, kind, data_type) { Ok(symbol) => values.push(HirVarDeclStmtValue { symbol, @@ -109,7 +111,7 @@ impl Analyzer { } HirVarDeclStmt { values, - data_type, + data_type: base_type, type_span: var_decl.type_span, } } @@ -121,12 +123,15 @@ impl Analyzer { } 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 mut parameter_types = vec![]; + for param in &func_decl.params { + parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true)); + } + let return_type: SemaType = func_decl.return_type.into(); let sig = FunctionSig { id: function_id, name: func_decl.name.clone(), - return_type, + return_type: return_type.clone(), parameter_types, }; self.function_map.insert(func_decl.name.clone(), function_id); @@ -137,8 +142,8 @@ impl Analyzer { 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) { + let param_type = self.build_var_type(param.param_type.into(), ¶m.dimensions, true); + match self.symbols.declare_variable(¶m.name, SymbolKind::Param, param_type.clone()) { Ok(symbol) => params.push(HirParam { symbol, param_type, @@ -162,6 +167,40 @@ impl Analyzer { }) } + fn build_var_type(&mut self, base_type: SemaType, dimensions: &[ArrayDimension], is_param: bool) -> SemaType { + if dimensions.is_empty() { + return base_type; + } + let mut dims = vec![]; + for (i, dimension) in dimensions.iter().enumerate() { + match &dimension.value { + Some(expr) => { + if let ExprValue::IntLit(value) = expr.value { + if value > 0 { + dims.push(value as usize); + } else { + self.add_error(SemaError::InvalidArrayDimension, dimension.span); + } + } else { + self.add_error(SemaError::InvalidArrayDimension, dimension.span); + } + } + None if is_param && i == 0 => {} + None => self.add_error(SemaError::InvalidArrayDimension, dimension.span), + } + } + if is_param { + let pointee_type = if dims.is_empty() { + base_type + } else { + SemaType::Array(Box::new(base_type), dims) + }; + SemaType::Ptr(Box::new(pointee_type)) + } else { + SemaType::Array(Box::new(base_type), dims) + } + } + fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt { let mut statements = vec![]; for stmt in block_stmt.statements { @@ -191,7 +230,7 @@ impl Analyzer { } fn analyze_return_stmt(&mut self, return_stmt: ReturnStmt) -> HirReturnStmt { - let expected_ty = self.current_func_return_type.unwrap(); + let expected_ty = self.current_func_return_type.clone().unwrap(); let value = match return_stmt.value { Some(expr) => { if expected_ty == SemaType::Void { @@ -202,8 +241,8 @@ impl Analyzer { 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); + } else if !self.type_matches(&expected_ty, &expr.ty) { + self.add_error(SemaError::TypeMismatch(expected_ty.clone(), expr.ty.clone()), return_stmt.span); } Some(expr) } @@ -304,11 +343,12 @@ impl Analyzer { }; Some(HirExpr { value: HirExprValue::Var(symbol), - ty: self.symbols.get_symbol(symbol).ty, + ty: self.symbols.get_symbol(symbol).ty.clone(), span, }) } ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span), + ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span), ExprValue::UnaryOp { op, operand } => { let operand_span = operand.span; let operand = self.analyze_expr(*operand)?; @@ -318,7 +358,7 @@ impl Analyzer { } let ty = match op { crate::ast::types::UnaryOp::Not => SemaType::I1, - _ => operand.ty, + _ => operand.ty.clone(), }; Some(HirExpr { value: HirExprValue::UnaryOp { @@ -335,19 +375,27 @@ impl Analyzer { } fn analyze_assign_expr(&mut self, lvalue: Expr, rvalue: Expr, span: Span) -> Option { - if !matches!(lvalue.value, ExprValue::Var(_)) { + if !matches!(lvalue.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) { self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span); return None; } let lvalue = self.analyze_expr(lvalue)?; + if lvalue.ty.is_array_like() { + self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span); + return None; + } 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; } + if !self.type_matches(&lvalue.ty, &rvalue.ty) { + self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), rvalue.ty.clone()), span); + return None; + } Some(HirExpr { - ty: lvalue.ty, + ty: lvalue.ty.clone(), value: HirExprValue::Assign { lvalue: Box::new(lvalue), rvalue: Box::new(rvalue), @@ -361,24 +409,24 @@ impl Analyzer { 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); + if !lhs.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(lhs.ty.clone()), lhs_span); return None; } - if rhs.ty == SemaType::Void { - self.add_error(SemaError::InvalidOperand(SemaType::Void), rhs_span); + if !rhs.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(rhs.ty.clone()), rhs_span); return None; } let result_ty = if op.is_logical() { SemaType::I1 } else { - match SemaType::get_elevate_result(lhs.ty, rhs.ty) { + 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); + self.add_error(SemaError::IncompatiableOperand(lhs.ty.clone(), rhs.ty.clone()), lhs_span); + self.add_error(SemaError::IncompatiableOperand(lhs.ty.clone(), rhs.ty.clone()), rhs_span); return None; } } @@ -428,9 +476,9 @@ impl Analyzer { 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); + let parameter_type = func_def.parameter_types[i].clone(); + if !self.type_matches(¶meter_type, &arg.ty) { + self.add_error(SemaError::TypeMismatch(parameter_type, arg.ty.clone()), span); has_error = true; continue; } @@ -446,4 +494,36 @@ impl Analyzer { span, }) } + + fn analyze_array_access_expr(&mut self, array: Expr, index: Expr, span: Span) -> Option { + let index_span = index.span; + let array = self.analyze_expr(array)?; + let index = self.analyze_expr(index)?; + if !index.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(index.ty.clone()), index_span); + return None; + } + let ty = match array.ty.indexed_type() { + Some(ty) => ty, + None => { + self.add_error(SemaError::NotSubscriptable, span); + return None; + } + }; + Some(HirExpr { + value: HirExprValue::ArrayAccess { + array: Box::new(array), + index: Box::new(index), + }, + ty, + span, + }) + } + + fn type_matches(&self, expected: &SemaType, actual: &SemaType) -> bool { + if expected == actual { + return true; + } + matches!((expected, actual), (SemaType::Ptr(expected_elem), SemaType::Array(_, _)) if actual.indexed_type().is_some_and(|ty| &ty == expected_elem.as_ref())) + } } diff --git a/src/sema/err.rs b/src/sema/err.rs index 13f8f3b..82cdde8 100644 --- a/src/sema/err.rs +++ b/src/sema/err.rs @@ -32,4 +32,8 @@ pub enum SemaError { InvalidParameterType(SemaType), #[error("return expression on void function")] ReturnExpressionOnVoidFunction, + #[error("array dimension must be a positive integer literal")] + InvalidArrayDimension, + #[error("subscripted value is not an array or pointer")] + NotSubscriptable, } diff --git a/src/sema/hir.rs b/src/sema/hir.rs index 3907ebc..34f90eb 100644 --- a/src/sema/hir.rs +++ b/src/sema/hir.rs @@ -89,6 +89,10 @@ pub struct HirExpr { pub enum HirExprValue { IntLit(i64), Var(SymbolId), + ArrayAccess { + array: Box, + index: Box, + }, BinaryOp { lhs: Box, op: BinaryOp, diff --git a/src/sema/types.rs b/src/sema/types.rs index 1334101..90a6333 100644 --- a/src/sema/types.rs +++ b/src/sema/types.rs @@ -2,23 +2,57 @@ use std::fmt::Display; use crate::{ast::types::Type as AstType, ir::types::IRType}; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum SemaType { I32, I1, Void, + Array(Box, Vec), + Ptr(Box), } impl SemaType { - pub fn get_elevate_result(lhs: SemaType, rhs: SemaType) -> Option { + 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(lhs.clone()) + } else if (*lhs == SemaType::I32 && *rhs == SemaType::I1) || (*lhs == SemaType::I1 && *rhs == SemaType::I32) { Some(SemaType::I32) } else { None } } + + pub fn is_scalar(&self) -> bool { + matches!(self, SemaType::I32 | SemaType::I1) + } + + pub fn is_array_like(&self) -> bool { + matches!(self, SemaType::Array(_, _) | SemaType::Ptr(_)) + } + + pub fn indexed_type(&self) -> Option { + match self { + SemaType::Array(elem, dims) => { + if dims.len() == 1 { + Some((**elem).clone()) + } else { + Some(SemaType::Array(elem.clone(), dims[1..].to_vec())) + } + } + SemaType::Ptr(elem) => Some((**elem).clone()), + _ => None, + } + } + + pub fn element_size_in_bytes(&self) -> usize { + match self { + SemaType::I32 => 4, + SemaType::I1 => 1, + SemaType::Void => 0, + SemaType::Array(elem, dims) => elem.element_size_in_bytes() * dims.iter().product::(), + SemaType::Ptr(_) => 4, + } + } } impl Display for SemaType { @@ -27,6 +61,14 @@ impl Display for SemaType { SemaType::I32 => write!(f, "i32"), SemaType::I1 => write!(f, "i1"), SemaType::Void => write!(f, "void"), + SemaType::Array(elem, dims) => { + write!(f, "{}", elem)?; + for dim in dims { + write!(f, "[{}]", dim)?; + } + Ok(()) + } + SemaType::Ptr(elem) => write!(f, "{}*", elem), } } } @@ -46,6 +88,8 @@ impl From for IRType { SemaType::I32 => IRType::I32, SemaType::I1 => IRType::I1, SemaType::Void => IRType::Void, + SemaType::Array(elem, dims) => IRType::Array(Box::new((*elem).into()), dims), + SemaType::Ptr(elem) => IRType::Ptr(Box::new((*elem).into())), } } }