diff --git a/src/ast/graph.rs b/src/ast/graph.rs index 10b9a5b..4bbd664 100644 --- a/src/ast/graph.rs +++ b/src/ast/graph.rs @@ -235,6 +235,12 @@ impl AstGraphBuilder { self.add_expr(node, rvalue); node }, + ExprValue::CompoundAssign { lvalue, rvalue, .. } => { + let node = self.child(parent, expr.value.to_string()); + self.add_expr(node, lvalue); + self.add_expr(node, rvalue); + node + }, ExprValue::UnaryOp { op: _, operand } => { let node = self.child(parent, expr.value.to_string()); self.add_expr(node, operand); diff --git a/src/ast/parser/expr.rs b/src/ast/parser/expr.rs index 7b3aeec..2b46579 100644 --- a/src/ast/parser/expr.rs +++ b/src/ast/parser/expr.rs @@ -344,12 +344,28 @@ impl Parser { } fn parse_assign(&mut self) -> Result { let lvalue = self.parse_logical_or()?; - if self.peek().value != TokenValue::Equal { - return Ok(lvalue); - } + let compound_op = match self.peek().value { + TokenValue::Equal => None, + TokenValue::PlusEqual => Some(BinaryOp::Add), + TokenValue::MinusEqual => Some(BinaryOp::Sub), + TokenValue::StarEqual => Some(BinaryOp::Mul), + TokenValue::SlashEqual => Some(BinaryOp::Div), + TokenValue::PercentEqual => Some(BinaryOp::Mod), + _ => return Ok(lvalue), + }; self.advance(1); let rvalue = self.parse_assign()?; let span = Span::from_two(lvalue.span, rvalue.span); + if let Some(op) = compound_op { + return Ok(Expr { + value: ExprValue::CompoundAssign { + lvalue: Box::new(lvalue), + op, + rvalue: Box::new(rvalue), + }, + span, + }); + } Ok(Expr { value: ExprValue::Assign { lvalue: Box::new(lvalue), diff --git a/src/ast/types.rs b/src/ast/types.rs index 5cbf27f..33baf18 100644 --- a/src/ast/types.rs +++ b/src/ast/types.rs @@ -104,10 +104,12 @@ pub struct ReturnStmt { pub value: Option, pub span: Span, } +#[derive(Clone)] pub struct Expr { pub value: ExprValue, pub span: Span, } +#[derive(Clone)] pub enum ExprValue { IntLit(i64), Var(String), @@ -134,6 +136,11 @@ pub enum ExprValue { lvalue: Box, rvalue: Box }, + CompoundAssign { + lvalue: Box, + op: BinaryOp, + rvalue: Box, + }, } #[derive(Clone, Copy)] pub enum BinaryOp { @@ -310,6 +317,7 @@ impl fmt::Display for ExprValue { ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op), ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name), ExprValue::Assign { .. } => write!(f, "Assign"), + ExprValue::CompoundAssign { op, .. } => write!(f, "CompoundAssign({})", op), ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op), ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op), } diff --git a/src/ir/generator.rs b/src/ir/generator.rs index 59183e0..200d52f 100644 --- a/src/ir/generator.rs +++ b/src/ir/generator.rs @@ -417,9 +417,48 @@ impl<'a> Generator<'a> { 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())); + instrs.push(IRInstr::Store(target.clone(), rvalue_var)); } else { - instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var.unwrap()))); + instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(rvalue_var))); + } + 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::CompoundAssign { lvalue, op, rvalue } => { + let lvalue_ty: IRType = lvalue.ty.clone().into(); + let (mut instrs, target, is_addr) = self.generate_lvalue(*lvalue)?; + let mut old_var = self.var_manager.declare_temp(lvalue_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()))); + } + + self.current_exit_label.push(None); + let (rvalue_instrs, rvalue_var) = self.generate_expr(*rvalue)?; + self.current_exit_label.pop(); + instrs.extend(rvalue_instrs); + let mut rvalue_var = rvalue_var.unwrap(); + + let convert_to = IRType::get_elevate_result(&old_var.data_type, &rvalue_var.data_type).unwrap(); + if convert_to != old_var.data_type { + old_var = self.coerce_value(&mut instrs, old_var, &convert_to); + } + if convert_to != rvalue_var.data_type { + rvalue_var = self.coerce_value(&mut instrs, rvalue_var, &convert_to); + } + let computed = self.var_manager.declare_temp(convert_to); + instrs.push(IRInstr::Binary(computed.clone(), old_var, op.into(), rvalue_var)); + let stored = self.coerce_value(&mut instrs, computed, &lvalue_ty); + if is_addr { + instrs.push(IRInstr::Store(target.clone(), stored)); + } else { + instrs.push(IRInstr::Move(target.clone(), MoveRValue::Var(stored))); } let temp_var = self.var_manager.declare_temp(lvalue_ty); if is_addr { diff --git a/src/lexer/types.rs b/src/lexer/types.rs index 4cb66bc..abc7d02 100644 --- a/src/lexer/types.rs +++ b/src/lexer/types.rs @@ -18,6 +18,7 @@ pub enum TokenValue { Plus, Minus, Star, Slash, Percent, PlusPlus, MinusMinus, + PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or, LParen, RParen, @@ -56,6 +57,11 @@ impl std::fmt::Display for TokenValue { TokenValue::Minus => write!(f, "`-`"), TokenValue::PlusPlus => write!(f, "`++`"), TokenValue::MinusMinus => write!(f, "`--`"), + TokenValue::PlusEqual => write!(f, "`+=`"), + TokenValue::MinusEqual => write!(f, "`-=`"), + TokenValue::StarEqual => write!(f, "`*=`"), + TokenValue::SlashEqual => write!(f, "`/=`"), + TokenValue::PercentEqual => write!(f, "`%=`"), TokenValue::Star => write!(f, "`*`"), TokenValue::Slash => write!(f, "`/`"), TokenValue::Percent => write!(f, "`%`"), @@ -98,6 +104,7 @@ pub enum TokenKind { TypeIdent, Plus, Minus, Star, Slash, Percent, + PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual, Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual, LParen, RParen, diff --git a/src/sema/analyzer.rs b/src/sema/analyzer.rs index e6b39f7..fad5e52 100644 --- a/src/sema/analyzer.rs +++ b/src/sema/analyzer.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use crate::{ ast::types::{ @@ -21,6 +21,7 @@ use crate::{ pub struct Analyzer { symbols: SymbolTable, function_map: BTreeMap, + builtin_functions: BTreeSet, functions: Vec, current_func_return_type: Option, diagnostic: Diagnositics, @@ -32,6 +33,7 @@ impl Analyzer { let mut analyzer = Self { symbols: SymbolTable::new(), function_map: BTreeMap::new(), + builtin_functions: BTreeSet::new(), functions: vec![], current_func_return_type: None, diagnostic: Diagnositics::new(), @@ -75,6 +77,7 @@ impl Analyzer { parameter_types, }; self.function_map.insert(name.to_string(), id); + self.builtin_functions.insert(id); self.functions.push(sig); } @@ -156,7 +159,11 @@ impl Analyzer { } fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option { - if self.function_map.contains_key(&func_decl.name) { + let is_static = func_decl.storage_class == Some(StorageClass::Static); + let existing_function = self.function_map.get(&func_decl.name).cloned(); + let can_shadow_builtin = is_static + && existing_function.is_some_and(|function| self.builtin_functions.contains(&function)); + if existing_function.is_some() && !can_shadow_builtin { self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span); return None; } @@ -167,9 +174,14 @@ impl Analyzer { parameter_types.push(self.build_var_type(param.param_type.into(), ¶m.dimensions, true)); } let return_type: SemaType = func_decl.return_type.into(); + let ir_name = if is_static { + format!("__static_func_{}_{}", function_id.0, func_decl.name) + } else { + func_decl.name.clone() + }; let sig = FunctionSig { id: function_id, - name: func_decl.name.clone(), + name: ir_name, return_type: return_type.clone(), parameter_types, }; @@ -208,24 +220,31 @@ impl Analyzer { fn eval_const_expr(expr: &Expr) -> Option { match &expr.value { - ExprValue::IntLit(value) => i32::try_from(*value).ok(), + ExprValue::IntLit(value) => Some(*value as i32), ExprValue::UnaryOp { op, operand } => { let value = Self::eval_const_expr(operand)?; match op { crate::ast::types::UnaryOp::Add => Some(value), - crate::ast::types::UnaryOp::Sub => value.checked_neg(), + crate::ast::types::UnaryOp::Sub => Some(value.wrapping_neg()), crate::ast::types::UnaryOp::Not => Some((value == 0) as i32), } } ExprValue::BinaryOp { lhs, op, rhs } => { let lhs = Self::eval_const_expr(lhs)?; + match op { + BinaryOp::And if lhs == 0 => return Some(0), + BinaryOp::Or if lhs != 0 => return Some(1), + _ => {} + } let rhs = Self::eval_const_expr(rhs)?; match op { - BinaryOp::Add => lhs.checked_add(rhs), - BinaryOp::Sub => lhs.checked_sub(rhs), - BinaryOp::Mul => lhs.checked_mul(rhs), - BinaryOp::Div => lhs.checked_div(rhs), - BinaryOp::Mod => lhs.checked_rem(rhs), + BinaryOp::Add => Some(lhs.wrapping_add(rhs)), + BinaryOp::Sub => Some(lhs.wrapping_sub(rhs)), + BinaryOp::Mul => Some(lhs.wrapping_mul(rhs)), + BinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)), + BinaryOp::Div => None, + BinaryOp::Mod if rhs != 0 => Some(lhs.wrapping_rem(rhs)), + BinaryOp::Mod => None, BinaryOp::Equal => Some((lhs == rhs) as i32), BinaryOp::NotEqual => Some((lhs != rhs) as i32), BinaryOp::Less => Some((lhs < rhs) as i32), @@ -248,12 +267,12 @@ impl Analyzer { for (i, dimension) in dimensions.iter().enumerate() { match &dimension.value { Some(expr) => { - if let ExprValue::IntLit(value) = expr.value { + if let Some(value) = Self::eval_const_expr(expr) { if value > 0 { dims.push(value as usize); - } else { - self.add_error(SemaError::InvalidArrayDimension, dimension.span); + continue; } + self.add_error(SemaError::InvalidArrayDimension, dimension.span); } else { self.add_error(SemaError::InvalidArrayDimension, dimension.span); } @@ -440,6 +459,7 @@ impl Analyzer { }) } ExprValue::Assign { lvalue, rvalue } => self.analyze_assign_expr(*lvalue, *rvalue, span), + ExprValue::CompoundAssign { lvalue, op, rvalue } => self.analyze_compound_assign_expr(*lvalue, op, *rvalue, span), ExprValue::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span), ExprValue::UnaryOp { op, operand } => { let operand_span = operand.span; @@ -450,6 +470,7 @@ impl Analyzer { } let ty = match op { crate::ast::types::UnaryOp::Not => SemaType::I1, + crate::ast::types::UnaryOp::Add | crate::ast::types::UnaryOp::Sub if operand.ty == SemaType::I1 => SemaType::I32, _ => operand.ty.clone(), }; Some(HirExpr { @@ -517,6 +538,50 @@ impl Analyzer { }) } + fn analyze_compound_assign_expr(&mut self, lvalue: Expr, op: BinaryOp, rvalue: Expr, span: Span) -> Option { + if !matches!(lvalue.value, ExprValue::Var(_) | ExprValue::ArrayAccess { .. }) { + self.add_error(SemaError::InvalidAssignmentTarget, lvalue.span); + return None; + } + let lvalue_span = lvalue.span; + 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 !lvalue.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(lvalue.ty.clone()), lvalue_span); + return None; + } + if !rvalue.ty.is_scalar() { + self.add_error(SemaError::InvalidOperand(rvalue.ty.clone()), rvalue_span); + return None; + } + let result_ty = match SemaType::get_elevate_result(&lvalue.ty, &rvalue.ty) { + Some(ty) => ty, + None => { + self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), lvalue_span); + self.add_error(SemaError::IncompatiableOperand(lvalue.ty.clone(), rvalue.ty.clone()), rvalue_span); + return None; + } + }; + if !self.type_matches(&lvalue.ty, &result_ty) { + self.add_error(SemaError::TypeMismatch(lvalue.ty.clone(), result_ty), span); + return None; + } + Some(HirExpr { + ty: lvalue.ty.clone(), + value: HirExprValue::CompoundAssign { + lvalue: Box::new(lvalue), + op, + rvalue: Box::new(rvalue), + }, + span, + }) + } + fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option { let lhs_span = lhs.span; let rhs_span = rhs.span; @@ -619,7 +684,7 @@ impl Analyzer { let ty = match array.ty.indexed_type() { Some(ty) => ty, None => { - self.add_error(SemaError::NotSubscriptable, span); + self.add_error(SemaError::NotSubscriptable(array.ty.clone()), span); return None; } }; @@ -637,6 +702,9 @@ impl Analyzer { if expected == actual { return true; } + if matches!((expected, actual), (SemaType::I32, SemaType::I1)) { + 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() diff --git a/src/sema/hir.rs b/src/sema/hir.rs index 133fbbc..3ad6f3b 100644 --- a/src/sema/hir.rs +++ b/src/sema/hir.rs @@ -127,4 +127,9 @@ pub enum HirExprValue { lvalue: Box, rvalue: Box, }, + CompoundAssign { + lvalue: Box, + op: BinaryOp, + rvalue: Box, + }, }