gut analyzer by moving the arity check to the typechecker

This commit is contained in:
2026-03-19 13:16:34 +01:00
parent 2d2a2c667f
commit e24763dfb6
3 changed files with 38 additions and 180 deletions

View File

@@ -1,7 +1,7 @@
use std::collections::HashMap;
use crate::{
parser::{Expr, Stmt},
parser::Stmt,
tokenizer::{ZernError, error},
};
@@ -62,39 +62,8 @@ impl Analyzer {
}
}
pub fn register_function(&mut self, stmt: &Stmt) -> Result<(), ZernError> {
if let Stmt::Function {
name,
params,
return_type,
body: _,
exported: _,
} = stmt
{
if self.functions.contains_key(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
}
self.functions.insert(
name.lexeme.clone(),
FnType {
return_type: return_type.lexeme.clone(),
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
},
);
}
Ok(())
}
pub fn analyze_stmt(&mut self, stmt: &Stmt) -> Result<(), ZernError> {
pub fn register_declaration(&mut self, stmt: &Stmt) -> Result<(), ZernError> {
match stmt {
Stmt::Expression(expr) => self.analyze_expr(expr)?,
Stmt::Let {
name: _,
var_type: _,
initializer,
} => {
self.analyze_expr(initializer)?;
}
Stmt::Const { name, value } => {
if self.constants.contains_key(&name.lexeme)
|| self.functions.contains_key(&name.lexeme)
@@ -114,57 +83,6 @@ impl Analyzer {
.insert(name.lexeme.clone(), value.lexeme.parse().unwrap());
}
}
Stmt::Block(statements) => {
for stmt in statements {
self.analyze_stmt(stmt)?;
}
}
Stmt::If {
keyword: _,
condition,
then_branch,
else_branch,
} => {
self.analyze_expr(condition)?;
self.analyze_stmt(then_branch)?;
self.analyze_stmt(else_branch)?;
}
Stmt::While {
keyword: _,
condition,
body,
} => {
self.analyze_expr(condition)?;
self.analyze_stmt(body)?;
}
Stmt::Function {
name,
params: _,
return_type,
body,
exported: _,
} => {
if name.lexeme == "main" && return_type.lexeme != "i64" {
return error!(&name.loc, "main must return i64");
}
self.analyze_stmt(body)?;
}
Stmt::Return { expr, keyword: _ } => {
self.analyze_expr(expr)?;
}
Stmt::For {
var: _,
start,
end,
body,
} => {
self.analyze_expr(start)?;
self.analyze_expr(end)?;
self.analyze_stmt(body)?;
}
Stmt::Break => {}
Stmt::Continue => {}
Stmt::Extern(name) => {
if self.functions.contains_key(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
@@ -172,6 +90,24 @@ impl Analyzer {
self.functions
.insert(name.lexeme.clone(), FnType::new_variadic("any"));
}
Stmt::Function {
name,
params,
return_type,
body: _,
exported: _,
} => {
if self.functions.contains_key(&name.lexeme) {
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
}
self.functions.insert(
name.lexeme.clone(),
FnType {
return_type: return_type.lexeme.clone(),
params: Some(params.iter().map(|x| x.var_type.lexeme.clone()).collect()),
},
);
}
Stmt::Struct { name, fields } => {
let mut fields_map: HashMap<String, StructField> = HashMap::new();
@@ -189,94 +125,7 @@ impl Analyzer {
self.structs.insert(name.lexeme.clone(), fields_map);
}
}
Ok(())
}
pub fn analyze_expr(&mut self, expr: &Expr) -> Result<(), ZernError> {
match expr {
Expr::Binary { left, op: _, right } => {
self.analyze_expr(left)?;
self.analyze_expr(right)?;
}
Expr::Logical { left, op: _, right } => {
self.analyze_expr(left)?;
self.analyze_expr(right)?;
}
Expr::Grouping(expr) => self.analyze_expr(expr)?,
Expr::Literal(_) => {}
Expr::Unary { op: _, right } => {
self.analyze_expr(right)?;
}
Expr::Variable(_) => {}
Expr::Assign { left, op: _, value } => {
self.analyze_expr(left)?;
self.analyze_expr(value)?;
}
Expr::Call {
callee,
paren,
args,
} => {
if let Expr::Variable(callee_name) = *callee.clone() {
if self.functions.contains_key(&callee_name.lexeme) {
// its a function (defined/builtin/extern)
if let Some(fn_type) = self.functions.get(&callee_name.lexeme) {
// if its None, its variadic
if let Some(params) = &fn_type.params
&& params.len() != args.len()
{
return error!(
&paren.loc,
format!(
"expected {} arguments, got {}",
params.len(),
args.len()
)
);
}
} else {
return error!(
&paren.loc,
format!("undefined function: {}", callee_name.lexeme)
);
}
} else {
// its a variable containing function address
self.analyze_expr(callee)?;
}
} else {
// its an expression that evalutes to function address
self.analyze_expr(callee)?;
}
for arg in args {
self.analyze_expr(arg)?;
}
}
Expr::ArrayLiteral(exprs) => {
for expr in exprs {
self.analyze_expr(expr)?;
}
}
Expr::Index {
expr,
bracket: _,
index,
} => {
self.analyze_expr(expr)?;
self.analyze_expr(index)?;
}
Expr::AddrOf { op: _, expr } => {
self.analyze_expr(expr)?;
}
Expr::New(_) => {}
Expr::MemberAccess { left, field: _ } => {
self.analyze_expr(left)?;
}
Expr::Cast { expr, type_name: _ } => {
self.analyze_expr(expr)?;
}
_ => {}
}
Ok(())
}

View File

@@ -46,10 +46,7 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
let mut analyzer = analyzer::Analyzer::new();
for stmt in &statements {
analyzer.register_function(stmt)?;
}
for stmt in &statements {
analyzer.analyze_stmt(stmt)?;
analyzer.register_declaration(stmt)?;
}
let mut typechecker = typechecker::TypeChecker::new(&analyzer);

View File

@@ -160,12 +160,16 @@ impl<'a> TypeChecker<'a> {
env.pop_scope();
}
Stmt::Function {
name: _,
name,
params,
return_type,
body,
exported: _,
} => {
if name.lexeme == "main" && return_type.lexeme != "i64" {
return error!(&name.loc, "main must return i64");
}
if !self.is_valid_type_name(&return_type.lexeme) {
return error!(
&return_type.loc,
@@ -369,12 +373,20 @@ impl<'a> TypeChecker<'a> {
args,
} => {
if let Expr::Variable(callee_name) = &**callee {
if self.analyzer.functions.contains_key(&callee_name.lexeme) {
let fn_type = &self.analyzer.functions[&callee_name.lexeme];
if let Some(fn_type) = self.analyzer.functions.get(&callee_name.lexeme) {
// its a function (defined/builtin/extern)
if let Some(params) = fn_type.params.clone() {
if params.len() != args.len() {
return error!(
&paren.loc,
format!(
"expected {} arguments, got {}",
params.len(),
args.len()
)
);
}
for (i, arg) in args.iter().enumerate() {
// arity is checked in the analyzer
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
}
} else {