diff --git a/src/ast/graph.rs b/src/ast/graph.rs index e477170..10b9a5b 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::{ - ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt + ArrayDimension, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt, GlobalDeclStmt, IfStmt, Param, ReturnStmt, Statement, VarDeclStmt, VarDeclStmtValue, WhileStmt }; pub type AstGraph = Graph; @@ -77,6 +77,9 @@ impl AstGraphBuilder { for dimension in &value.dimensions { self.add_array_dimension(node, dimension); } + if let Some(expr) = &value.value { + self.add_expr(node, expr); + } node } @@ -134,6 +137,7 @@ impl AstGraphBuilder { }, Statement::If(if_stmt) => self.add_if_stmt(parent, if_stmt), Statement::While(while_stmt) => self.add_while_stmt(parent, while_stmt), + Statement::For(for_stmt) => self.add_for_stmt(parent, for_stmt), Statement::Break(break_stmt) => self.add_break_stmt(parent, break_stmt), Statement::Continue(continue_stmt) => self.add_continue_stmt(parent, continue_stmt), } @@ -160,6 +164,42 @@ impl AstGraphBuilder { self.add_block_stmt(node, &while_stmt.body); node } + fn add_for_stmt(&mut self, parent: NodeIndex, for_stmt: &ForStmt) -> NodeIndex { + let node = self.child(parent, for_stmt.to_string()); + match &for_stmt.init { + Some(ForInit::Expr(expr)) => { + let init = self.child(node, "Init"); + self.add_expr(init, expr); + } + Some(ForInit::VarDecl(var_decl)) => { + let init = self.child(node, "Init"); + self.add_var_decl(init, var_decl); + } + None => { + self.child(node, "NoInit"); + } + } + match &for_stmt.condition { + Some(expr) => { + let condition = self.child(node, "Condition"); + self.add_expr(condition, expr); + } + None => { + self.child(node, "NoCondition"); + } + } + match &for_stmt.update { + Some(expr) => { + let update = self.child(node, "Update"); + self.add_expr(update, expr); + } + None => { + self.child(node, "NoUpdate"); + } + } + self.add_block_stmt(node, &for_stmt.body); + node + } fn add_break_stmt(&mut self, parent: NodeIndex, break_stmt: &BreakStmt) -> NodeIndex { self.child(parent, break_stmt.to_string()) } @@ -200,6 +240,11 @@ impl AstGraphBuilder { self.add_expr(node, operand); node } + ExprValue::IncDec { operand, .. } => { + let node = self.child(parent, expr.value.to_string()); + self.add_expr(node, operand); + node + } } } } diff --git a/src/ast/parser.rs b/src/ast/parser.rs index f9d65e6..48f5c67 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -1,6 +1,6 @@ use crate::{ ast::types::{ - ArrayDimension, 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, ForInit, ForStmt, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, IncDecOp, Param, ReturnStmt, Statement, UnaryOp, VarDeclStmt, VarDeclStmtValue, WhileStmt }, diagnostic::{Diagnositics, span::Span}, lexer::{ @@ -46,6 +46,9 @@ impl Parser { fn peek(&self) -> &Token { self.tokens.get(self.pos).unwrap_or(&self.eof_token) } + fn peek_n(&self, n: usize) -> &Token { + self.tokens.get(self.pos + n).unwrap_or(&self.eof_token) + } fn next(&mut self) -> Token { let token = self.peek().clone(); if !matches!(token.value, TokenValue::Eof) { @@ -294,6 +297,10 @@ impl Parser { // } // } fn parse_var_decl_stmt(&mut self, parse_type: ParseType) -> Result { + self.parse_var_decl_stmt_inner(parse_type, true) + } + + fn parse_var_decl_stmt_inner(&mut self, parse_type: ParseType, consume_semicolon: bool) -> Result { assert!(!self.is_eof()); let mut values = vec![]; let (var_type, name, type_span, name_span) = match self.parse_type_and_name(parse_type) { @@ -307,7 +314,13 @@ impl Parser { } }; let dimensions = self.parse_array_dimensions(false)?; - values.push(VarDeclStmtValue { name, name_span, dimensions }); + let value = if self.peek().value == TokenValue::Equal { + self.advance(1); + Some(self.parse_expr()?) + } else { + None + }; + values.push(VarDeclStmtValue { name, name_span, dimensions, value }); let mut last_name = true; // indicate whether the last parsed token is a variable name while !self.is_eof() { if matches!(self.peek().value, TokenValue::Semicolon) { // statement end @@ -327,7 +340,13 @@ impl Parser { if let Some(ident) = self.peek().value.as_ident() { let span = self.next().span; let dimensions = self.parse_array_dimensions(false)?; - values.push(VarDeclStmtValue { name: ident, name_span: span, dimensions }); + let value = if self.peek().value == TokenValue::Equal { + self.advance(1); + Some(self.parse_expr()?) + } else { + None + }; + values.push(VarDeclStmtValue { name: ident, name_span: span, dimensions, value }); last_name = true; } else { let token = self.next().clone(); @@ -338,7 +357,9 @@ impl Parser { return Err(ParseProcessError::ErrorInMatch); } } - self.must_match_token(&TokenValue::Semicolon, "`;`")?; + if consume_semicolon { + self.must_match_token(&TokenValue::Semicolon, "`;`")?; + } Ok(VarDeclStmt { values, type_span, data_type: var_type.into() }) } @@ -457,6 +478,11 @@ impl Parser { Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch), Err(_) => {}, } + match self.parse_for_stmt() { + Ok(for_stmt) => return Ok(Statement::For(for_stmt)), + Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch), + Err(_) => {}, + } match self.parse_break_stmt() { Ok(break_stmt) => return Ok(Statement::Break(break_stmt)), Err(ParseProcessError::ErrorInMatch) => return Err(ParseProcessError::ErrorInMatch), @@ -593,6 +619,48 @@ impl Parser { }) } + fn parse_for_stmt(&mut self) -> Result { + assert!(!self.is_eof()); + if self.peek().value != TokenValue::For { + return Err(ParseProcessError::TryNext); + } + self.advance(1); + self.must_match_token(&TokenValue::LParen, "`(`")?; + let init = if self.peek().value == TokenValue::Semicolon { + None + } else if matches!(self.peek().value, TokenValue::TypeIdent(_)) { + Some(ForInit::VarDecl(self.parse_var_decl_stmt_inner(ParseType::MustParse, false)?)) + } else { + Some(ForInit::Expr(self.parse_expr()?)) + }; + self.must_match_token(&TokenValue::Semicolon, "`;`")?; + let condition = if self.peek().value == TokenValue::Semicolon { + None + } else { + Some(self.parse_expr()?) + }; + self.must_match_token(&TokenValue::Semicolon, "`;`")?; + let update = if self.peek().value == TokenValue::RParen { + None + } else { + Some(self.parse_expr()?) + }; + self.must_match_token(&TokenValue::RParen, "`)`")?; + let body; + if self.peek().value != TokenValue::LBrace { + let stmt = self.parse_stmt()?; + body = BlockStmt { statements: vec![stmt] }; + } else { + body = self.parse_block_stmt(ParseType::MustParse)?; + } + Ok(ForStmt { + init, + condition, + update, + body, + }) + } + fn parse_break_stmt(&mut self) -> Result { assert!(!self.is_eof()); let start_span = self.peek().span; @@ -747,41 +815,63 @@ impl Parser { } fn parse_primary(&mut self) -> Result { let mut expr = self.parse_primary_atom()?; - while self.peek().value == TokenValue::LBracket { - let start_span = expr.span; - self.advance(1); - let index = if self.is_eof() { - self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("array index expression"), start_span); - return Err(ParseProcessError::ErrorInMatch); - } else { - self.parse_expr()? - }; - let end_span = match &self.peek().value { - TokenValue::RBracket => { - let span = self.peek().span; - self.advance(1); - span - } - TokenValue::Eof => { - self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), index.span); - return Err(ParseProcessError::ErrorInMatch); - } - _ => { - let token = self.next(); - self.diagnostics.add_from_frontend_error( - ParseError::UnexpectedToken(token.value, "`]`"), - token.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), + loop { + if self.peek().value == TokenValue::LBracket { + let start_span = expr.span; + self.advance(1); + let index = if self.is_eof() { + self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("array index expression"), start_span); + return Err(ParseProcessError::ErrorInMatch); + } else { + self.parse_expr()? + }; + let end_span = match &self.peek().value { + TokenValue::RBracket => { + let span = self.peek().span; + self.advance(1); + span + } + TokenValue::Eof => { + self.diagnostics.add_from_frontend_error(ParseError::ExpectButEof("`]`"), index.span); + return Err(ParseProcessError::ErrorInMatch); + } + _ => { + let token = self.next(); + self.diagnostics.add_from_frontend_error( + ParseError::UnexpectedToken(token.value, "`]`"), + token.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), + }; + continue; + } + let op = match (&self.peek().value, &self.peek_n(1).value) { + (TokenValue::Plus, TokenValue::Plus) => Some(IncDecOp::Inc), + (TokenValue::Minus, TokenValue::Minus) => Some(IncDecOp::Dec), + _ => None, }; + if let Some(op) = op { + let end_span = self.peek_n(1).span; + self.advance(2); + expr = Expr { + span: Span::from_two(expr.span, end_span), + value: ExprValue::IncDec { + op, + operand: Box::new(expr), + is_prefix: false, + }, + }; + continue; + } + break; } Ok(expr) } @@ -790,6 +880,27 @@ impl Parser { let token = self.peek().clone(); match token.value { TokenValue::Plus => { + if self.peek_n(1).value == TokenValue::Plus { + self.advance(2); + let expr = if self.is_eof() { + self.diagnostics.add_from_frontend_error( + ParseError::ExpectButEof("expression"), + token.span, + ); + return Err(ParseProcessError::ErrorInMatch); + } else { + self.parse_unary()? + }; + let span = Span::from_two(token.span, expr.span); + return Ok(Expr { + value: ExprValue::IncDec { + op: IncDecOp::Inc, + operand: Box::new(expr), + is_prefix: true, + }, + span, + }); + } self.advance(1); let expr = if self.is_eof() { self.diagnostics.add_from_frontend_error( @@ -810,6 +921,27 @@ impl Parser { }) } TokenValue::Minus => { + if self.peek_n(1).value == TokenValue::Minus { + self.advance(2); + let rhs = if self.is_eof() { + self.diagnostics.add_from_frontend_error( + ParseError::ExpectButEof("expression"), + token.span, + ); + return Err(ParseProcessError::ErrorInMatch); + } else { + self.parse_unary()? + }; + let span = Span::from_two(token.span, rhs.span); + return Ok(Expr { + value: ExprValue::IncDec { + op: IncDecOp::Dec, + operand: Box::new(rhs), + is_prefix: true, + }, + span, + }); + } self.advance(1); let rhs = if self.is_eof() { self.diagnostics.add_from_frontend_error( diff --git a/src/ast/types.rs b/src/ast/types.rs index 72260eb..0383f8f 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -18,6 +18,7 @@ pub struct VarDeclStmtValue { pub name: String, pub name_span: Span, pub dimensions: Vec, + pub value: Option, } pub struct ArrayDimension { @@ -44,6 +45,7 @@ pub enum Statement { VarDecl(VarDeclStmt), If(IfStmt), While(WhileStmt), + For(ForStmt), Break(BreakStmt), Continue(ContinueStmt), } @@ -77,6 +79,16 @@ pub struct WhileStmt { pub body: BlockStmt, // pub span: Span, } +pub struct ForStmt { + pub init: Option, + pub condition: Option, + pub update: Option, + pub body: BlockStmt, +} +pub enum ForInit { + Expr(Expr), + VarDecl(VarDeclStmt), +} pub struct BreakStmt { pub span: Span, } @@ -107,6 +119,11 @@ pub enum ExprValue { op: UnaryOp, operand: Box, }, + IncDec { + op: IncDecOp, + operand: Box, + is_prefix: bool, + }, FuncCall(String, Vec), Assign { lvalue: Box, @@ -124,6 +141,11 @@ pub enum BinaryOp { pub enum UnaryOp { Add, Sub, Not, } +#[derive(Clone, Copy)] +pub enum IncDecOp { + Inc, + Dec, +} impl BinaryOp { pub fn from_token_value(token_value: &TokenValue) -> Option { match token_value { @@ -227,6 +249,7 @@ impl fmt::Display for Statement { Statement::VarDecl(_) => write!(f, "VarDeclStmt"), Statement::If(_) => write!(f, "IfStmt"), Statement::While(_) => write!(f, "WhileStmt"), + Statement::For(_) => write!(f, "ForStmt"), Statement::Break(_) => write!(f, "BreakStmt"), Statement::Continue(_) => write!(f, "ContinueStmt"), } @@ -249,6 +272,12 @@ impl fmt::Display for WhileStmt { } } +impl fmt::Display for ForStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ForStmt") + } +} + impl fmt::Display for BreakStmt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "BreakStmt") @@ -277,6 +306,7 @@ impl fmt::Display for ExprValue { ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name), ExprValue::Assign { .. } => write!(f, "Assign"), ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op), + ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op), } } } @@ -317,6 +347,14 @@ impl fmt::Display for UnaryOp { write!(f, "{}", op) } } +impl fmt::Display for IncDecOp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + IncDecOp::Inc => write!(f, "Inc"), + IncDecOp::Dec => write!(f, "Dec"), + } + } +} impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/backend/generator.rs b/src/backend/generator.rs index 6e41240..77d716d 100644 --- a/src/backend/generator.rs +++ b/src/backend/generator.rs @@ -78,6 +78,23 @@ fn emit_store_stack(src: Register, offset: i32, reg_allocator: &mut RegisterAllo fn load_variable(variable: Variable, reg_allocator: &mut RegisterAllocator, var_index_to_stack_offset: &BTreeMap, instrs: &mut Vec) -> RegisterAlloc { if variable.data_type.is_array() { let var_alloc = reg_allocator.alloc(variable.clone()).expect("Ran out of registers"); + if variable.data_type.is_array_param() { + match variable.var_type { + VariableType::ParamTemp(param_index) => { + if param_index < ARG_REGS.len() { + instrs.push(MoveInstr::new_uncond(var_alloc.reg, RegisterOrImm::Reg(ARG_REGS[param_index]))); + } else { + let stack_arg_offset = 8 + ((param_index - ARG_REGS.len()) * 4); + instrs.push(LoadInstr::new(var_alloc.reg, REG_FP, Some(RegisterOrImm::Imm(stack_arg_offset as i32)))); + } + } + _ => { + let stack_offset = var_index_to_stack_offset.get(&variable.index).expect("Variable not declared"); + emit_load_stack(var_alloc.reg, *stack_offset as i32, reg_allocator, instrs); + } + } + return var_alloc; + } match variable.var_type { VariableType::Global => { instrs.push(LoadPseudoInstr::new(var_alloc.reg, format!("global_var_{}", variable.index))); @@ -213,7 +230,7 @@ impl Generator { IRInstr::Store(addr, value) => self.emit_store(addr, value, &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(); + let size = if variable.data_type.is_array_param() { 4 } else { variable.data_type.size_in_bytes() }; stack_size_needed = (stack_size_needed + size).next_multiple_of(ARM_STACK_ALIGNMENT); var_index_to_stack_offset.insert(variable.index, stack_size_needed); }, diff --git a/src/ir/generator.rs b/src/ir/generator.rs index b81fb6f..281145e 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, vec}; -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::{ir::types::{BinaryOp as IRBinaryOp, CmpOp, Function, IRInstr, IRType, MoveRValue, UnaryOp, Variable, VariableOrIntLit, VariableType}, sema::{analyzer::Analyzer as SemaAnalyzer, hir::{HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, HirForInit, HirForStmt, 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> { @@ -64,11 +64,24 @@ impl<'a> Generator<'a> { fn generate_var_decl(&mut self, var_decl: HirVarDeclStmt, is_global: bool) -> Vec { let mut instrs = vec![]; - let var_type = if is_global { VariableType::Global } else { VariableType::Local }; for value in var_decl.values { + let var_type = if is_global { VariableType::Global } else { VariableType::Local }; 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)); + } else if let Some(init) = value.value { + self.current_exit_label.push(None); + let (init_instrs, init_var) = match self.generate_expr(init) { + Some(res) => res, + None => { + self.current_exit_label.pop(); + continue; + } + }; + self.current_exit_label.pop(); + let target = self.var_manager.get_symbol(value.symbol).unwrap(); + instrs.extend(init_instrs); + instrs.push(IRInstr::Move(target, MoveRValue::Var(init_var.unwrap()))); } } @@ -125,6 +138,7 @@ impl<'a> Generator<'a> { Return(return_stmt) => self.generate_return_stmt(return_stmt), If(if_stmt) => self.generate_if_stmt(if_stmt), While(while_stmt) => self.generate_while_stmt(while_stmt), + For(for_stmt) => self.generate_for_stmt(for_stmt), Break(break_stmt) => self.generate_break_stmt(break_stmt), Continue(continue_stmt) => self.generate_continue_stmt(continue_stmt), Block(block_stmt) => { @@ -193,6 +207,69 @@ impl<'a> Generator<'a> { instrs.push(IRInstr::Label(exit_label)); instrs } + fn generate_for_stmt(&mut self, for_stmt: HirForStmt) -> Vec { + let mut instrs = vec![]; + let cond_label = self.request_label(); + let body_label = self.request_label(); + let update_label = self.request_label(); + let exit_label = self.request_label(); + + if let Some(init) = for_stmt.init { + match init { + HirForInit::Expr(init) => { + self.current_exit_label.push(None); + let (init_instrs, _) = match self.generate_expr(init) { + Some(res) => res, + None => { + self.current_exit_label.pop(); + return vec![]; + } + }; + self.current_exit_label.pop(); + instrs.extend(init_instrs); + } + HirForInit::VarDecl(var_decl) => { + instrs.extend(self.generate_var_decl(var_decl, false)); + } + } + } + + instrs.push(IRInstr::Label(cond_label)); + if let Some(condition) = for_stmt.condition { + self.current_exit_label.push(Some((body_label, exit_label))); + let (cond_instrs, _) = match self.generate_expr(condition) { + Some(res) => res, + None => { + self.current_exit_label.pop(); + return vec![]; + } + }; + self.current_exit_label.pop(); + instrs.extend(cond_instrs); + } + + instrs.push(IRInstr::Label(body_label)); + self.while_exit_label.push((update_label, exit_label)); + instrs.extend(self.generate_block_stmt(for_stmt.body)); + self.while_exit_label.pop(); + + instrs.push(IRInstr::Label(update_label)); + if let Some(update) = for_stmt.update { + self.current_exit_label.push(None); + let (update_instrs, _) = match self.generate_expr(update) { + Some(res) => res, + None => { + self.current_exit_label.pop(); + return vec![]; + } + }; + self.current_exit_label.pop(); + instrs.extend(update_instrs); + } + instrs.push(IRInstr::Goto(cond_label)); + instrs.push(IRInstr::Label(exit_label)); + instrs + } fn generate_if_stmt(&mut self, if_stmt: HirIfStmt) -> Vec { let mut instrs = vec![]; let then_label = self.request_label(); @@ -274,9 +351,13 @@ impl<'a> Generator<'a> { HirExprValue::Var(symbol) => { let var = self.var_manager.get_symbol(symbol).unwrap(); 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::Move(dest.clone(), MoveRValue::Var(var))], Some(dest)) + if var.data_type.is_array_param() { + (vec![], Some(var)) + } else { + let ptr_ty = var.data_type.decay_to_ptr().unwrap(); + let dest = self.var_manager.declare_temp(ptr_ty); + (vec![IRInstr::Move(dest.clone(), MoveRValue::Var(var))], Some(dest)) + } } else { (vec![], Some(var)) } @@ -372,6 +453,35 @@ impl<'a> Generator<'a> { }; (instrs, Some(dest_var)) }, + HirExprValue::IncDec { op, operand, is_prefix } => { + let result_ty: IRType = expr.ty.clone().into(); + let (mut instrs, target, is_addr) = self.generate_lvalue(*operand)?; + let old_var = self.var_manager.declare_temp(result_ty.clone()); + if is_addr { + instrs.push(IRInstr::Load(old_var.clone(), target.clone())); + } else { + instrs.push(IRInstr::Move(old_var.clone(), MoveRValue::Var(target.clone()))); + } + + let one = self.var_manager.declare_temp(result_ty.clone()); + instrs.push(IRInstr::Move(one.clone(), MoveRValue::ConstInt(1))); + let new_var = self.var_manager.declare_temp(result_ty.clone()); + let ir_op = match op { + crate::ast::types::IncDecOp::Inc => IRBinaryOp::Add, + crate::ast::types::IncDecOp::Dec => IRBinaryOp::Sub, + }; + instrs.push(IRInstr::Binary(new_var.clone(), old_var.clone(), ir_op, one)); + if is_addr { + instrs.push(IRInstr::Store(target, new_var.clone())); + } else { + instrs.push(IRInstr::Move(target, MoveRValue::Var(new_var.clone()))); + } + if is_prefix { + (instrs, Some(new_var)) + } else { + (instrs, Some(old_var)) + } + }, HirExprValue::BinaryOp { lhs, op, rhs } => { // +--------+-------------------------+-------------------------+-------------------------+ // | parent | (true_exit, false_exit) | (true_exit, false_exit) | (true_exit, false_exit) | @@ -564,8 +674,12 @@ impl<'a> Generator<'a> { 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::Move(ptr.clone(), MoveRValue::Var(var))], ptr) + if var.data_type.is_array_param() { + (vec![], var) + } else { + let ptr = self.var_manager.declare_temp(var.data_type.decay_to_ptr().unwrap()); + (vec![IRInstr::Move(ptr.clone(), MoveRValue::Var(var))], ptr) + } } else { (vec![], var) } diff --git a/src/ir/types.rs b/src/ir/types.rs index 3e4bd44..e6d401c 100644 --- a/src/ir/types.rs +++ b/src/ir/types.rs @@ -86,6 +86,10 @@ impl IRType { matches!(self, IRType::Array(_, _)) } + pub fn is_array_param(&self) -> bool { + matches!(self, IRType::Array(_, dims) if dims.first().is_some_and(|dim| *dim == 0)) + } + pub fn decay_to_ptr(&self) -> Option { match self { IRType::Array(elem, dims) => { @@ -236,6 +240,18 @@ impl Variable { _ => format!("{} {}", self.data_type, self), } } + + pub fn to_typed_string_with_type(&self, ty: &IRType) -> String { + match ty { + IRType::Array(_, _) => { + let (base_type, dims) = ty.base_type_and_dims(); + let dims_str = dims.iter().map(|dim| format!("[{}]", dim)).collect::>().join(""); + format!("{} {}{}", base_type, self, dims_str) + } + IRType::Ptr(_) => format!("{}* {}", ty.scalar_base_type(), self), + _ => format!("{} {}", ty, self), + } + } } #[derive(Debug, Clone)] pub struct Function { @@ -247,7 +263,7 @@ pub struct Function { impl Function { pub fn to_call_string(&self, args: &Vec) -> String { assert!(args.len() == self.parameter_types.len()); - let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| format!("{} {}", param, arg)).collect::>().join(", "); + let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| arg.to_typed_string_with_type(param)).collect::>().join(", "); format!("{} @{}({})", self.return_type, self.name, args_str) } diff --git a/src/lexer/lexer.rs b/src/lexer/lexer.rs index ce865c6..a279a8d 100644 --- a/src/lexer/lexer.rs +++ b/src/lexer/lexer.rs @@ -345,6 +345,9 @@ fn parse_ident( if name.eq("while") { return Ok(TokenValue::While); } + if name.eq("for") { + return Ok(TokenValue::For); + } if name.eq("break") { return Ok(TokenValue::Break); } diff --git a/src/lexer/types.rs b/src/lexer/types.rs index 0789bf4..532ae7e 100644 --- a/src/lexer/types.rs +++ b/src/lexer/types.rs @@ -24,7 +24,7 @@ pub enum TokenValue { LBracket, RBracket, Comma, Semicolon, - If, Else, While, Return, Break, Continue, + If, Else, While, For, Return, Break, Continue, Eof, Unrecognized, @@ -77,6 +77,7 @@ impl std::fmt::Display for TokenValue { TokenValue::If => write!(f, "if"), TokenValue::Else => write!(f, "else"), TokenValue::While => write!(f, "while"), + TokenValue::For => write!(f, "for"), TokenValue::Return => write!(f, "return"), TokenValue::Break => write!(f, "break"), TokenValue::Continue => write!(f, "continue"), @@ -100,7 +101,7 @@ pub enum TokenKind { LBracket, RBracket, Comma, Semicolon, - If, Else, While, Return, Break, Continue, + If, Else, While, For, Return, Break, Continue, Eof, Unrecognized, diff --git a/src/sema/analyzer.rs b/src/sema/analyzer.rs index fbd0c34..0b67c33 100644 --- a/src/sema/analyzer.rs +++ b/src/sema/analyzer.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use crate::{ ast::types::{ - ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, FuncDeclStmt, + ArrayDimension, BinaryOp, BlockStmt, BreakStmt, CompileUnit, ContinueStmt, Expr, ExprValue, ForInit, ForStmt, FuncDeclStmt, GlobalDeclStmt, IfElseBranch, IfStmt, ReturnStmt, Statement, VarDeclStmt, WhileStmt, }, diagnostic::{span::Span, Diagnositics}, @@ -10,7 +10,7 @@ use crate::{ err::SemaError, hir::{ HirBlockStmt, HirBreakStmt, HirCompileUnit, HirContinueStmt, HirExpr, HirExprValue, - HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam, + HirForInit, HirForStmt, HirFuncDeclStmt, HirGlobalDeclStmt, HirIfElseBranch, HirIfStmt, HirParam, HirReturnStmt, HirStatement, HirVarDeclStmt, HirVarDeclStmtValue, HirWhileStmt, }, symbol::{FunctionId, FunctionSig, SymbolId, SymbolKind, SymbolTable}, @@ -39,7 +39,9 @@ impl Analyzer { }; analyzer.declare_builtin_func("putint", vec![SemaType::I32], SemaType::Void); analyzer.declare_builtin_func("putch", vec![SemaType::I32], SemaType::Void); + analyzer.declare_builtin_func("putarray", vec![SemaType::I32, SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::Void); analyzer.declare_builtin_func("getint", vec![], SemaType::I32); + analyzer.declare_builtin_func("getarray", vec![SemaType::Array(Box::new(SemaType::I32), vec![0])], SemaType::I32); analyzer } @@ -102,10 +104,24 @@ impl Analyzer { 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, - name_span: value.name_span, - }), + Ok(symbol) => { + let symbol_type = self.symbols.get_symbol(symbol).ty.clone(); + let init = value.value.and_then(|expr| { + let span = expr.span; + let init = self.analyze_expr(expr)?; + if init.ty == SemaType::Void { + self.add_error(SemaError::InvalidOperand(SemaType::Void), span); + } else if !self.type_matches(&symbol_type, &init.ty) { + self.add_error(SemaError::TypeMismatch(symbol_type.clone(), init.ty.clone()), span); + } + Some(init) + }); + values.push(HirVarDeclStmtValue { + symbol, + name_span: value.name_span, + value: init, + }); + } Err(e) => self.add_error(e, value.name_span), } } @@ -185,20 +201,11 @@ impl Analyzer { self.add_error(SemaError::InvalidArrayDimension, dimension.span); } } - None if is_param && i == 0 => {} + None if is_param && i == 0 => dims.push(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) - } + SemaType::Array(Box::new(base_type), dims) } fn analyze_block_stmt(&mut self, block_stmt: BlockStmt) -> HirBlockStmt { @@ -216,6 +223,7 @@ impl Analyzer { 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::For(stmt) => Some(HirStatement::For(self.analyze_for_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) => { @@ -294,6 +302,26 @@ impl Analyzer { } } + fn analyze_for_stmt(&mut self, for_stmt: ForStmt) -> HirForStmt { + self.symbols.enter_scope(); + let init = for_stmt.init.and_then(|init| match init { + ForInit::Expr(expr) => self.analyze_expr(expr).map(HirForInit::Expr), + ForInit::VarDecl(var_decl) => Some(HirForInit::VarDecl(self.analyze_var_decl(var_decl, SymbolKind::Local))), + }); + let condition = for_stmt.condition.map(|expr| self.analyze_condition_expr(expr)); + let update = for_stmt.update.and_then(|expr| self.analyze_expr(expr)); + self.loop_depth += 1; + let body = self.analyze_block_stmt(for_stmt.body); + self.loop_depth -= 1; + self.symbols.exit_scope(); + HirForStmt { + init, + condition, + update, + body, + } + } + fn analyze_break_stmt(&mut self, stmt: BreakStmt) -> HirBreakStmt { if self.loop_depth == 0 { self.add_error(SemaError::BreakOutsideLoop, stmt.span); @@ -369,6 +397,27 @@ impl Analyzer { span, }) } + ExprValue::IncDec { op, operand, is_prefix } => { + if !matches!(operand.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) { + self.add_error(SemaError::InvalidAssignmentTarget, operand.span); + return None; + } + let operand_span = operand.span; + let operand = self.analyze_expr(*operand)?; + if !operand.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(operand.ty.clone()), operand_span); + return None; + } + Some(HirExpr { + ty: operand.ty.clone(), + value: HirExprValue::IncDec { + op, + operand: Box::new(operand), + is_prefix, + }, + 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), } @@ -524,6 +573,11 @@ impl Analyzer { if expected == actual { return true; } + if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) { + return expected_elem == actual_elem + && expected_dims.len() == actual_dims.len() + && expected_dims.iter().zip(actual_dims.iter()).all(|(expected_dim, actual_dim)| *expected_dim == 0 || expected_dim == actual_dim); + } 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/hir.rs b/src/sema/hir.rs index 34f90eb..04adeb5 100644 --- a/src/sema/hir.rs +++ b/src/sema/hir.rs @@ -18,6 +18,7 @@ pub struct HirVarDeclStmt { pub struct HirVarDeclStmtValue { pub symbol: SymbolId, pub name_span: Span, + pub value: Option, } pub struct HirFuncDeclStmt { @@ -46,6 +47,7 @@ pub enum HirStatement { VarDecl(HirVarDeclStmt), If(HirIfStmt), While(HirWhileStmt), + For(HirForStmt), Break(HirBreakStmt), Continue(HirContinueStmt), } @@ -67,6 +69,18 @@ pub struct HirWhileStmt { pub body: HirBlockStmt, } +pub struct HirForStmt { + pub init: Option, + pub condition: Option, + pub update: Option, + pub body: HirBlockStmt, +} + +pub enum HirForInit { + Expr(HirExpr), + VarDecl(HirVarDeclStmt), +} + pub struct HirBreakStmt { pub span: Span, } @@ -102,6 +116,11 @@ pub enum HirExprValue { op: UnaryOp, operand: Box, }, + IncDec { + op: crate::ast::types::IncDecOp, + operand: Box, + is_prefix: bool, + }, FuncCall(FunctionId, Vec), Assign { lvalue: Box, diff --git a/src/sema/types.rs b/src/sema/types.rs index 90a6333..8b575b7 100644 --- a/src/sema/types.rs +++ b/src/sema/types.rs @@ -35,6 +35,8 @@ impl SemaType { SemaType::Array(elem, dims) => { if dims.len() == 1 { Some((**elem).clone()) + } else if dims.first().is_some_and(|dim| *dim == 0) { + Some(SemaType::Array(elem.clone(), dims[1..].to_vec())) } else { Some(SemaType::Array(elem.clone(), dims[1..].to_vec())) }