feat(lexer,sema,ir): Add compoundassign

This commit is contained in:
2026-06-13 09:09:18 +08:00
parent 1de7e2876c
commit 09a99bbc33
7 changed files with 168 additions and 19 deletions
+6
View File
@@ -235,6 +235,12 @@ impl AstGraphBuilder {
self.add_expr(node, rvalue); self.add_expr(node, rvalue);
node 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 } => { ExprValue::UnaryOp { op: _, operand } => {
let node = self.child(parent, expr.value.to_string()); let node = self.child(parent, expr.value.to_string());
self.add_expr(node, operand); self.add_expr(node, operand);
+19 -3
View File
@@ -344,12 +344,28 @@ impl Parser {
} }
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> { fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
let lvalue = self.parse_logical_or()?; let lvalue = self.parse_logical_or()?;
if self.peek().value != TokenValue::Equal { let compound_op = match self.peek().value {
return Ok(lvalue); 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); self.advance(1);
let rvalue = self.parse_assign()?; let rvalue = self.parse_assign()?;
let span = Span::from_two(lvalue.span, rvalue.span); 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 { Ok(Expr {
value: ExprValue::Assign { value: ExprValue::Assign {
lvalue: Box::new(lvalue), lvalue: Box::new(lvalue),
+8
View File
@@ -104,10 +104,12 @@ pub struct ReturnStmt {
pub value: Option<Expr>, pub value: Option<Expr>,
pub span: Span, pub span: Span,
} }
#[derive(Clone)]
pub struct Expr { pub struct Expr {
pub value: ExprValue, pub value: ExprValue,
pub span: Span, pub span: Span,
} }
#[derive(Clone)]
pub enum ExprValue { pub enum ExprValue {
IntLit(i64), IntLit(i64),
Var(String), Var(String),
@@ -134,6 +136,11 @@ pub enum ExprValue {
lvalue: Box<Expr>, lvalue: Box<Expr>,
rvalue: Box<Expr> rvalue: Box<Expr>
}, },
CompoundAssign {
lvalue: Box<Expr>,
op: BinaryOp,
rvalue: Box<Expr>,
},
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum BinaryOp { pub enum BinaryOp {
@@ -310,6 +317,7 @@ impl fmt::Display for ExprValue {
ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op), ExprValue::BinaryOp { op, .. } => write!(f, "BinaryOp({})", op),
ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name), ExprValue::FuncCall(name, _) => write!(f, "FuncCall({})", name),
ExprValue::Assign { .. } => write!(f, "Assign"), ExprValue::Assign { .. } => write!(f, "Assign"),
ExprValue::CompoundAssign { op, .. } => write!(f, "CompoundAssign({})", op),
ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op), ExprValue::UnaryOp { op, .. } => write!(f, "UnaryOp({})", op),
ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op), ExprValue::IncDec { op, is_prefix, .. } => write!(f, "{}{}", if *is_prefix { "Prefix" } else { "Postfix" }, op),
} }
+41 -2
View File
@@ -417,9 +417,48 @@ impl<'a> Generator<'a> {
let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?; let (lvalue_instrs, target, is_addr) = self.generate_lvalue(*lvalue)?;
instrs.extend(lvalue_instrs); instrs.extend(lvalue_instrs);
if is_addr { if is_addr {
instrs.push(IRInstr::Store(target.clone(), rvalue_var.unwrap())); instrs.push(IRInstr::Store(target.clone(), rvalue_var));
} else { } 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); let temp_var = self.var_manager.declare_temp(lvalue_ty);
if is_addr { if is_addr {
+7
View File
@@ -18,6 +18,7 @@ pub enum TokenValue {
Plus, Minus, Star, Slash, Percent, Plus, Minus, Star, Slash, Percent,
PlusPlus, MinusMinus, PlusPlus, MinusMinus,
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or, Equal, DoubleEqual, Not, NotEqual, Less, LessEqual, Greater, GreaterEqual, And, Or,
LParen, RParen, LParen, RParen,
@@ -56,6 +57,11 @@ impl std::fmt::Display for TokenValue {
TokenValue::Minus => write!(f, "`-`"), TokenValue::Minus => write!(f, "`-`"),
TokenValue::PlusPlus => write!(f, "`++`"), TokenValue::PlusPlus => write!(f, "`++`"),
TokenValue::MinusMinus => 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::Star => write!(f, "`*`"),
TokenValue::Slash => write!(f, "`/`"), TokenValue::Slash => write!(f, "`/`"),
TokenValue::Percent => write!(f, "`%`"), TokenValue::Percent => write!(f, "`%`"),
@@ -98,6 +104,7 @@ pub enum TokenKind {
TypeIdent, TypeIdent,
Plus, Minus, Star, Slash, Percent, Plus, Minus, Star, Slash, Percent,
PlusEqual, MinusEqual, StarEqual, SlashEqual, PercentEqual,
Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual, Equal, DoubleEqual, NotEqual, Less, LessEqual, Greater, GreaterEqual,
LParen, RParen, LParen, RParen,
+82 -14
View File
@@ -1,4 +1,4 @@
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use crate::{ use crate::{
ast::types::{ ast::types::{
@@ -21,6 +21,7 @@ use crate::{
pub struct Analyzer { pub struct Analyzer {
symbols: SymbolTable, symbols: SymbolTable,
function_map: BTreeMap<String, FunctionId>, function_map: BTreeMap<String, FunctionId>,
builtin_functions: BTreeSet<FunctionId>,
functions: Vec<FunctionSig>, functions: Vec<FunctionSig>,
current_func_return_type: Option<SemaType>, current_func_return_type: Option<SemaType>,
diagnostic: Diagnositics, diagnostic: Diagnositics,
@@ -32,6 +33,7 @@ impl Analyzer {
let mut analyzer = Self { let mut analyzer = Self {
symbols: SymbolTable::new(), symbols: SymbolTable::new(),
function_map: BTreeMap::new(), function_map: BTreeMap::new(),
builtin_functions: BTreeSet::new(),
functions: vec![], functions: vec![],
current_func_return_type: None, current_func_return_type: None,
diagnostic: Diagnositics::new(), diagnostic: Diagnositics::new(),
@@ -75,6 +77,7 @@ impl Analyzer {
parameter_types, parameter_types,
}; };
self.function_map.insert(name.to_string(), id); self.function_map.insert(name.to_string(), id);
self.builtin_functions.insert(id);
self.functions.push(sig); self.functions.push(sig);
} }
@@ -156,7 +159,11 @@ impl Analyzer {
} }
fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> { fn analyze_func_decl(&mut self, func_decl: FuncDeclStmt) -> Option<HirFuncDeclStmt> {
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); self.add_error(SemaError::FunctionHasBeenDefined(func_decl.name.clone()), func_decl.name_span);
return None; return None;
} }
@@ -167,9 +174,14 @@ impl Analyzer {
parameter_types.push(self.build_var_type(param.param_type.into(), &param.dimensions, true)); parameter_types.push(self.build_var_type(param.param_type.into(), &param.dimensions, true));
} }
let return_type: SemaType = func_decl.return_type.into(); 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 { let sig = FunctionSig {
id: function_id, id: function_id,
name: func_decl.name.clone(), name: ir_name,
return_type: return_type.clone(), return_type: return_type.clone(),
parameter_types, parameter_types,
}; };
@@ -208,24 +220,31 @@ impl Analyzer {
fn eval_const_expr(expr: &Expr) -> Option<i32> { fn eval_const_expr(expr: &Expr) -> Option<i32> {
match &expr.value { match &expr.value {
ExprValue::IntLit(value) => i32::try_from(*value).ok(), ExprValue::IntLit(value) => Some(*value as i32),
ExprValue::UnaryOp { op, operand } => { ExprValue::UnaryOp { op, operand } => {
let value = Self::eval_const_expr(operand)?; let value = Self::eval_const_expr(operand)?;
match op { match op {
crate::ast::types::UnaryOp::Add => Some(value), 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), crate::ast::types::UnaryOp::Not => Some((value == 0) as i32),
} }
} }
ExprValue::BinaryOp { lhs, op, rhs } => { ExprValue::BinaryOp { lhs, op, rhs } => {
let lhs = Self::eval_const_expr(lhs)?; 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)?; let rhs = Self::eval_const_expr(rhs)?;
match op { match op {
BinaryOp::Add => lhs.checked_add(rhs), BinaryOp::Add => Some(lhs.wrapping_add(rhs)),
BinaryOp::Sub => lhs.checked_sub(rhs), BinaryOp::Sub => Some(lhs.wrapping_sub(rhs)),
BinaryOp::Mul => lhs.checked_mul(rhs), BinaryOp::Mul => Some(lhs.wrapping_mul(rhs)),
BinaryOp::Div => lhs.checked_div(rhs), BinaryOp::Div if rhs != 0 => Some(lhs.wrapping_div(rhs)),
BinaryOp::Mod => lhs.checked_rem(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::Equal => Some((lhs == rhs) as i32),
BinaryOp::NotEqual => Some((lhs != rhs) as i32), BinaryOp::NotEqual => Some((lhs != rhs) as i32),
BinaryOp::Less => 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() { for (i, dimension) in dimensions.iter().enumerate() {
match &dimension.value { match &dimension.value {
Some(expr) => { Some(expr) => {
if let ExprValue::IntLit(value) = expr.value { if let Some(value) = Self::eval_const_expr(expr) {
if value > 0 { if value > 0 {
dims.push(value as usize); dims.push(value as usize);
} else { continue;
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
} }
self.add_error(SemaError::InvalidArrayDimension, dimension.span);
} else { } else {
self.add_error(SemaError::InvalidArrayDimension, dimension.span); 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::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::ArrayAccess { array, index } => self.analyze_array_access_expr(*array, *index, span),
ExprValue::UnaryOp { op, operand } => { ExprValue::UnaryOp { op, operand } => {
let operand_span = operand.span; let operand_span = operand.span;
@@ -450,6 +470,7 @@ impl Analyzer {
} }
let ty = match op { let ty = match op {
crate::ast::types::UnaryOp::Not => SemaType::I1, 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(), _ => operand.ty.clone(),
}; };
Some(HirExpr { Some(HirExpr {
@@ -517,6 +538,50 @@ impl Analyzer {
}) })
} }
fn analyze_compound_assign_expr(&mut self, lvalue: Expr, op: BinaryOp, rvalue: Expr, span: Span) -> Option<HirExpr> {
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<HirExpr> { fn analyze_binary_expr(&mut self, lhs: Expr, op: BinaryOp, rhs: Expr, span: Span) -> Option<HirExpr> {
let lhs_span = lhs.span; let lhs_span = lhs.span;
let rhs_span = rhs.span; let rhs_span = rhs.span;
@@ -619,7 +684,7 @@ impl Analyzer {
let ty = match array.ty.indexed_type() { let ty = match array.ty.indexed_type() {
Some(ty) => ty, Some(ty) => ty,
None => { None => {
self.add_error(SemaError::NotSubscriptable, span); self.add_error(SemaError::NotSubscriptable(array.ty.clone()), span);
return None; return None;
} }
}; };
@@ -637,6 +702,9 @@ impl Analyzer {
if expected == actual { if expected == actual {
return true; 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) { if let (SemaType::Array(expected_elem, expected_dims), SemaType::Array(actual_elem, actual_dims)) = (expected, actual) {
return expected_elem == actual_elem return expected_elem == actual_elem
&& expected_dims.len() == actual_dims.len() && expected_dims.len() == actual_dims.len()
+5
View File
@@ -127,4 +127,9 @@ pub enum HirExprValue {
lvalue: Box<HirExpr>, lvalue: Box<HirExpr>,
rvalue: Box<HirExpr>, rvalue: Box<HirExpr>,
}, },
CompoundAssign {
lvalue: Box<HirExpr>,
op: BinaryOp,
rvalue: Box<HirExpr>,
},
} }