gut analyzer by moving the arity check to the typechecker
This commit is contained in:
193
src/analyzer.rs
193
src/analyzer.rs
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Expr, Stmt},
|
parser::Stmt,
|
||||||
tokenizer::{ZernError, error},
|
tokenizer::{ZernError, error},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,39 +62,8 @@ impl Analyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_function(&mut self, stmt: &Stmt) -> Result<(), ZernError> {
|
pub fn register_declaration(&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> {
|
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expression(expr) => self.analyze_expr(expr)?,
|
|
||||||
Stmt::Let {
|
|
||||||
name: _,
|
|
||||||
var_type: _,
|
|
||||||
initializer,
|
|
||||||
} => {
|
|
||||||
self.analyze_expr(initializer)?;
|
|
||||||
}
|
|
||||||
Stmt::Const { name, value } => {
|
Stmt::Const { name, value } => {
|
||||||
if self.constants.contains_key(&name.lexeme)
|
if self.constants.contains_key(&name.lexeme)
|
||||||
|| self.functions.contains_key(&name.lexeme)
|
|| self.functions.contains_key(&name.lexeme)
|
||||||
@@ -114,57 +83,6 @@ impl Analyzer {
|
|||||||
.insert(name.lexeme.clone(), value.lexeme.parse().unwrap());
|
.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) => {
|
Stmt::Extern(name) => {
|
||||||
if self.functions.contains_key(&name.lexeme) {
|
if self.functions.contains_key(&name.lexeme) {
|
||||||
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
return error!(name.loc, format!("tried to redefine '{}'", name.lexeme));
|
||||||
@@ -172,6 +90,24 @@ impl Analyzer {
|
|||||||
self.functions
|
self.functions
|
||||||
.insert(name.lexeme.clone(), FnType::new_variadic("any"));
|
.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 } => {
|
Stmt::Struct { name, fields } => {
|
||||||
let mut fields_map: HashMap<String, StructField> = HashMap::new();
|
let mut fields_map: HashMap<String, StructField> = HashMap::new();
|
||||||
|
|
||||||
@@ -189,94 +125,7 @@ impl Analyzer {
|
|||||||
|
|
||||||
self.structs.insert(name.lexeme.clone(), fields_map);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,10 +46,7 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
|
|||||||
|
|
||||||
let mut analyzer = analyzer::Analyzer::new();
|
let mut analyzer = analyzer::Analyzer::new();
|
||||||
for stmt in &statements {
|
for stmt in &statements {
|
||||||
analyzer.register_function(stmt)?;
|
analyzer.register_declaration(stmt)?;
|
||||||
}
|
|
||||||
for stmt in &statements {
|
|
||||||
analyzer.analyze_stmt(stmt)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut typechecker = typechecker::TypeChecker::new(&analyzer);
|
let mut typechecker = typechecker::TypeChecker::new(&analyzer);
|
||||||
|
|||||||
@@ -160,12 +160,16 @@ impl<'a> TypeChecker<'a> {
|
|||||||
env.pop_scope();
|
env.pop_scope();
|
||||||
}
|
}
|
||||||
Stmt::Function {
|
Stmt::Function {
|
||||||
name: _,
|
name,
|
||||||
params,
|
params,
|
||||||
return_type,
|
return_type,
|
||||||
body,
|
body,
|
||||||
exported: _,
|
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) {
|
if !self.is_valid_type_name(&return_type.lexeme) {
|
||||||
return error!(
|
return error!(
|
||||||
&return_type.loc,
|
&return_type.loc,
|
||||||
@@ -369,12 +373,20 @@ impl<'a> TypeChecker<'a> {
|
|||||||
args,
|
args,
|
||||||
} => {
|
} => {
|
||||||
if let Expr::Variable(callee_name) = &**callee {
|
if let Expr::Variable(callee_name) = &**callee {
|
||||||
if self.analyzer.functions.contains_key(&callee_name.lexeme) {
|
if let Some(fn_type) = self.analyzer.functions.get(&callee_name.lexeme) {
|
||||||
let fn_type = &self.analyzer.functions[&callee_name.lexeme];
|
|
||||||
// its a function (defined/builtin/extern)
|
// its a function (defined/builtin/extern)
|
||||||
if let Some(params) = fn_type.params.clone() {
|
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() {
|
for (i, arg) in args.iter().enumerate() {
|
||||||
// arity is checked in the analyzer
|
|
||||||
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
|
expect_type!(self.typecheck_expr(env, arg)?, params[i], paren.loc);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user