feat(parser,ir,backend): Support array

This commit is contained in:
2026-05-31 22:06:09 +08:00
parent c42575c1c6
commit 669c415bd7
13 changed files with 575 additions and 130 deletions
+106 -26
View File
@@ -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::<Vec<_>>();
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(), &param.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(&param.name, SymbolKind::Param, param_type) {
let param_type = self.build_var_type(param.param_type.into(), &param.dimensions, true);
match self.symbols.declare_variable(&param.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<HirExpr> {
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(&parameter_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<HirExpr> {
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()))
}
}