feat(parser,ir,backend): Support array
This commit is contained in:
+106
-26
@@ -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(), ¶m.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(¶m.name, SymbolKind::Param, param_type) {
|
||||
let param_type = self.build_var_type(param.param_type.into(), ¶m.dimensions, true);
|
||||
match self.symbols.declare_variable(¶m.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(¶meter_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()))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user