feat(lexer,sema,ir): Add compoundassign
This commit is contained in:
@@ -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);
|
||||
|
||||
+19
-3
@@ -344,12 +344,28 @@ impl Parser {
|
||||
}
|
||||
fn parse_assign(&mut self) -> Result<Expr, ParseProcessError> {
|
||||
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),
|
||||
|
||||
@@ -104,10 +104,12 @@ pub struct ReturnStmt {
|
||||
pub value: Option<Expr>,
|
||||
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<Expr>,
|
||||
rvalue: Box<Expr>
|
||||
},
|
||||
CompoundAssign {
|
||||
lvalue: Box<Expr>,
|
||||
op: BinaryOp,
|
||||
rvalue: Box<Expr>,
|
||||
},
|
||||
}
|
||||
#[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),
|
||||
}
|
||||
|
||||
+41
-2
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
+82
-14
@@ -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<String, FunctionId>,
|
||||
builtin_functions: BTreeSet<FunctionId>,
|
||||
functions: Vec<FunctionSig>,
|
||||
current_func_return_type: Option<SemaType>,
|
||||
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<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);
|
||||
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<i32> {
|
||||
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<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> {
|
||||
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()
|
||||
|
||||
@@ -127,4 +127,9 @@ pub enum HirExprValue {
|
||||
lvalue: Box<HirExpr>,
|
||||
rvalue: Box<HirExpr>,
|
||||
},
|
||||
CompoundAssign {
|
||||
lvalue: Box<HirExpr>,
|
||||
op: BinaryOp,
|
||||
rvalue: Box<HirExpr>,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user