Finalize error handling
This commit is contained in:
parent
3e4d3fad7d
commit
e67eeaaa81
5 changed files with 165 additions and 95 deletions
|
@ -26,7 +26,7 @@ mod variable;
|
||||||
pub fn compile(code: &str) -> Result<String, Box<dyn Error>> {
|
pub fn compile(code: &str) -> Result<String, Box<dyn Error>> {
|
||||||
let tokens = tokenize(code)?;
|
let tokens = tokenize(code)?;
|
||||||
let mut ast = parse(&tokens)?;
|
let mut ast = parse(&tokens)?;
|
||||||
type_check(&mut ast, &mut SymTab::new_type_table());
|
type_check(&mut ast, &mut SymTab::new_type_table())?;
|
||||||
let ir = generate_ir(&ast);
|
let ir = generate_ir(&ast);
|
||||||
let assembly = generate_assembly(&ir);
|
let assembly = generate_assembly(&ir);
|
||||||
|
|
||||||
|
@ -38,10 +38,7 @@ pub fn start_compiler() {
|
||||||
for line in lines.map_while(Result::ok) {
|
for line in lines.map_while(Result::ok) {
|
||||||
match compile(&line) {
|
match compile(&line) {
|
||||||
Ok(_) => println!("\nCompilation OK :)\n"),
|
Ok(_) => println!("\nCompilation OK :)\n"),
|
||||||
Err(e) => println!(
|
Err(e) => println!("\n{e}\n"),
|
||||||
"{}",
|
|
||||||
format!("{{\"error\": {}}}", json::stringify(format!("{e}")))
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,17 @@ use crate::compiler::{
|
||||||
variable::Value,
|
variable::Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Function was made as an exercise mid-way through the project and has been left mostly as-is
|
||||||
|
// since!
|
||||||
|
|
||||||
pub fn interpret<'source>(ast: &AstNode<'source>, symbols: &mut SymTab<'source, Value>) -> Value {
|
pub fn interpret<'source>(ast: &AstNode<'source>, symbols: &mut SymTab<'source, Value>) -> Value {
|
||||||
match &ast.expr {
|
match &ast.expr {
|
||||||
EmptyLiteral() => Value::None(),
|
EmptyLiteral() => Value::None(),
|
||||||
IntLiteral(val) => Value::Int(*val),
|
IntLiteral(val) => Value::Int(*val),
|
||||||
BoolLiteral(val) => Value::Bool(*val),
|
BoolLiteral(val) => Value::Bool(*val),
|
||||||
Identifier(name) => *symbols.get(name),
|
Identifier(name) => *symbols.get(name).unwrap(),
|
||||||
UnaryOp(op, expr) => {
|
UnaryOp(op, expr) => {
|
||||||
let Value::Func(op_fn) = symbols.get(&format!("unary_{op}")) else {
|
let Value::Func(op_fn) = symbols.get(&format!("unary_{op}")).unwrap() else {
|
||||||
panic!("Operator {} does not correspond to a function!", op);
|
panic!("Operator {} does not correspond to a function!", op);
|
||||||
};
|
};
|
||||||
op_fn(&[interpret(expr, symbols)])
|
op_fn(&[interpret(expr, symbols)])
|
||||||
|
@ -57,14 +60,14 @@ pub fn interpret<'source>(ast: &AstNode<'source>, symbols: &mut SymTab<'source,
|
||||||
"=" => {
|
"=" => {
|
||||||
if let Expression::Identifier(name) = left.expr {
|
if let Expression::Identifier(name) = left.expr {
|
||||||
let val = interpret(right, symbols);
|
let val = interpret(right, symbols);
|
||||||
*symbols.get(name) = val;
|
*symbols.get(name).unwrap() = val;
|
||||||
val
|
val
|
||||||
} else {
|
} else {
|
||||||
panic!("Assignment must have identifier as left expr!");
|
panic!("Assignment must have identifier as left expr!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let Value::Func(op_fn) = symbols.get(op) else {
|
let Value::Func(op_fn) = symbols.get(op).unwrap() else {
|
||||||
panic!("Operator {} does not correspond to a function!", op);
|
panic!("Operator {} does not correspond to a function!", op);
|
||||||
};
|
};
|
||||||
op_fn(&[interpret(left, symbols), interpret(right, symbols)])
|
op_fn(&[interpret(left, symbols), interpret(right, symbols)])
|
||||||
|
@ -72,7 +75,7 @@ pub fn interpret<'source>(ast: &AstNode<'source>, symbols: &mut SymTab<'source,
|
||||||
},
|
},
|
||||||
VarDeclaration(name, expr, _) => {
|
VarDeclaration(name, expr, _) => {
|
||||||
let val = interpret(expr, symbols);
|
let val = interpret(expr, symbols);
|
||||||
symbols.insert(name, val);
|
symbols.insert(name, val).unwrap();
|
||||||
Value::None()
|
Value::None()
|
||||||
}
|
}
|
||||||
Conditional(condition_expr, then_expr, else_expr) => {
|
Conditional(condition_expr, then_expr, else_expr) => {
|
||||||
|
@ -114,7 +117,7 @@ pub fn interpret<'source>(ast: &AstNode<'source>, symbols: &mut SymTab<'source,
|
||||||
arg_values.push(interpret(arg, symbols));
|
arg_values.push(interpret(arg, symbols));
|
||||||
}
|
}
|
||||||
|
|
||||||
let Value::Func(function) = symbols.get(name) else {
|
let Value::Func(function) = symbols.get(name).unwrap() else {
|
||||||
panic!("Identifier {} does not correspond to a function!", name);
|
panic!("Identifier {} does not correspond to a function!", name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec<IrInstruction> {
|
||||||
let global_types = IrVar::new_global_types();
|
let global_types = IrVar::new_global_types();
|
||||||
let mut types = global_types.clone();
|
let mut types = global_types.clone();
|
||||||
for var in global_types.keys() {
|
for var in global_types.keys() {
|
||||||
symbols.insert(&var.name, var.clone());
|
symbols.insert(&var.name, var.clone()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = visit_ast_node(
|
let result = visit_ast_node(
|
||||||
|
@ -34,7 +34,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec<IrInstruction> {
|
||||||
match types.get(&result) {
|
match types.get(&result) {
|
||||||
Some(Type::Int) => {
|
Some(Type::Int) => {
|
||||||
let loc = instructions.last().unwrap().loc;
|
let loc = instructions.last().unwrap().loc;
|
||||||
let fn_var = symbols.get("print_int").clone();
|
let fn_var = symbols.get("print_int").unwrap().clone();
|
||||||
let result_var = add_var(&Type::Bool, &mut types);
|
let result_var = add_var(&Type::Bool, &mut types);
|
||||||
|
|
||||||
instructions.push(IrInstruction::new(
|
instructions.push(IrInstruction::new(
|
||||||
|
@ -44,7 +44,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec<IrInstruction> {
|
||||||
}
|
}
|
||||||
Some(Type::Bool) => {
|
Some(Type::Bool) => {
|
||||||
let loc = instructions.last().unwrap().loc;
|
let loc = instructions.last().unwrap().loc;
|
||||||
let fn_var = symbols.get("print_bool").clone();
|
let fn_var = symbols.get("print_bool").unwrap().clone();
|
||||||
let result_var = add_var(&Type::Bool, &mut types);
|
let result_var = add_var(&Type::Bool, &mut types);
|
||||||
|
|
||||||
instructions.push(IrInstruction::new(
|
instructions.push(IrInstruction::new(
|
||||||
|
@ -121,9 +121,9 @@ fn visit_ast_node<'source>(
|
||||||
});
|
});
|
||||||
var
|
var
|
||||||
}
|
}
|
||||||
Identifier(name) => symbols.get(name).clone(),
|
Identifier(name) => symbols.get(name).unwrap().clone(),
|
||||||
UnaryOp(op, expr) => {
|
UnaryOp(op, expr) => {
|
||||||
let op_var = symbols.get(&format!("unary_{op}")).clone();
|
let op_var = symbols.get(&format!("unary_{op}")).unwrap().clone();
|
||||||
let expr_var = visit_ast_node(expr, types, symbols, instructions, labels);
|
let expr_var = visit_ast_node(expr, types, symbols, instructions, labels);
|
||||||
let result_var = add_var(&ast.node_type, types);
|
let result_var = add_var(&ast.node_type, types);
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ fn visit_ast_node<'source>(
|
||||||
let Identifier(var_name) = left.expr else {
|
let Identifier(var_name) = left.expr else {
|
||||||
panic!("Tried to assign to non-variable!");
|
panic!("Tried to assign to non-variable!");
|
||||||
};
|
};
|
||||||
let var = symbols.get(var_name).clone();
|
let var = symbols.get(var_name).unwrap().clone();
|
||||||
|
|
||||||
instructions.push(IrInstruction::new(right.loc, Copy(right_var, var.clone())));
|
instructions.push(IrInstruction::new(right.loc, Copy(right_var, var.clone())));
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ fn visit_ast_node<'source>(
|
||||||
result_var
|
result_var
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let op_var = symbols.get(op).clone();
|
let op_var = symbols.get(op).unwrap().clone();
|
||||||
let left_var = visit_ast_node(left, types, symbols, instructions, labels);
|
let left_var = visit_ast_node(left, types, symbols, instructions, labels);
|
||||||
let right_var = visit_ast_node(right, types, symbols, instructions, labels);
|
let right_var = visit_ast_node(right, types, symbols, instructions, labels);
|
||||||
let result_var = add_var(&ast.node_type, types);
|
let result_var = add_var(&ast.node_type, types);
|
||||||
|
@ -231,7 +231,7 @@ fn visit_ast_node<'source>(
|
||||||
VarDeclaration(name, expr, _) => {
|
VarDeclaration(name, expr, _) => {
|
||||||
let expr_var = visit_ast_node(expr, types, symbols, instructions, labels);
|
let expr_var = visit_ast_node(expr, types, symbols, instructions, labels);
|
||||||
let result_var = add_var(&expr.node_type, types);
|
let result_var = add_var(&expr.node_type, types);
|
||||||
symbols.insert(name, result_var.clone());
|
symbols.insert(name, result_var.clone()).unwrap();
|
||||||
instructions.push(IrInstruction::new(expr.loc, Copy(expr_var, result_var)));
|
instructions.push(IrInstruction::new(expr.loc, Copy(expr_var, result_var)));
|
||||||
add_var(&Type::Unit, types)
|
add_var(&Type::Unit, types)
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ fn visit_ast_node<'source>(
|
||||||
add_var(&Type::Unit, types)
|
add_var(&Type::Unit, types)
|
||||||
}
|
}
|
||||||
FunCall(name, expressions) => {
|
FunCall(name, expressions) => {
|
||||||
let fn_var = symbols.get(name).clone();
|
let fn_var = symbols.get(name).unwrap().clone();
|
||||||
let Type::Func(_, result_type) = types.get(&fn_var).unwrap().clone() else {
|
let Type::Func(_, result_type) = types.get(&fn_var).unwrap().clone() else {
|
||||||
panic!("Function call does not have entry in types table!");
|
panic!("Function call does not have entry in types table!");
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,18 @@
|
||||||
use crate::compiler::variable::{Type, Value};
|
use crate::compiler::variable::{Type, Value};
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, error::Error, fmt::Display};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SymbolTableError {
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SymbolTableError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "SymbolTableError: {}", self.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for SymbolTableError {}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SymTab<'source, T> {
|
pub struct SymTab<'source, T> {
|
||||||
|
@ -7,13 +20,15 @@ pub struct SymTab<'source, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'source, T> SymTab<'source, T> {
|
impl<'source, T> SymTab<'source, T> {
|
||||||
pub fn get(&mut self, symbol: &str) -> &mut T {
|
pub fn get(&mut self, symbol: &str) -> Result<&mut T, SymbolTableError> {
|
||||||
for i in (0..self.tables.len()).rev() {
|
for i in (0..self.tables.len()).rev() {
|
||||||
if self.tables[i].contains_key(symbol) {
|
if self.tables[i].contains_key(symbol) {
|
||||||
return self.tables[i].get_mut(symbol).unwrap();
|
return Ok(self.tables[i].get_mut(symbol).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic!("No symbol {} found!", symbol);
|
Err(SymbolTableError {
|
||||||
|
msg: format!("No symbol {} found!", symbol),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_level(&mut self) {
|
pub fn push_level(&mut self) {
|
||||||
|
@ -24,7 +39,7 @@ impl<'source, T> SymTab<'source, T> {
|
||||||
self.tables.pop();
|
self.tables.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, name: &'source str, val: T) {
|
pub fn insert(&mut self, name: &'source str, val: T) -> Result<(), SymbolTableError> {
|
||||||
if self
|
if self
|
||||||
.tables
|
.tables
|
||||||
.last_mut()
|
.last_mut()
|
||||||
|
@ -32,7 +47,11 @@ impl<'source, T> SymTab<'source, T> {
|
||||||
.insert(name, val)
|
.insert(name, val)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
panic!("Variable {} already defined in this scope!", name)
|
Err(SymbolTableError {
|
||||||
|
msg: format!("Variable {} already defined in this scope!", name),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1,115 @@
|
||||||
|
use std::{error::Error, fmt::Display};
|
||||||
|
|
||||||
use crate::compiler::{
|
use crate::compiler::{
|
||||||
ast::{AstNode, Expression::*, TypeExpression},
|
ast::{AstNode, Expression::*, TypeExpression},
|
||||||
symtab::SymTab,
|
symtab::SymTab,
|
||||||
variable::Type,
|
variable::Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TypeCheckerError {
|
||||||
|
msg: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for TypeCheckerError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "TypeCheckerError: {}", self.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for TypeCheckerError {}
|
||||||
|
|
||||||
pub fn type_check<'source>(
|
pub fn type_check<'source>(
|
||||||
ast: &mut AstNode<'source>,
|
ast: &mut AstNode<'source>,
|
||||||
symbols: &mut SymTab<'source, Type>,
|
symbols: &mut SymTab<'source, Type>,
|
||||||
) -> Type {
|
) -> Result<Type, Box<dyn Error>> {
|
||||||
let node_type = get_type(ast, symbols);
|
let node_type = get_type(ast, symbols)?;
|
||||||
ast.node_type = node_type.clone();
|
ast.node_type = node_type.clone();
|
||||||
node_type
|
Ok(node_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_type<'source>(ast: &mut AstNode<'source>, symbols: &mut SymTab<'source, Type>) -> Type {
|
fn get_type<'source>(
|
||||||
|
ast: &mut AstNode<'source>,
|
||||||
|
symbols: &mut SymTab<'source, Type>,
|
||||||
|
) -> Result<Type, Box<dyn Error>> {
|
||||||
match &mut ast.expr {
|
match &mut ast.expr {
|
||||||
EmptyLiteral() => Type::Unit,
|
EmptyLiteral() => Ok(Type::Unit),
|
||||||
IntLiteral(_) => Type::Int,
|
IntLiteral(_) => Ok(Type::Int),
|
||||||
BoolLiteral(_) => Type::Bool,
|
BoolLiteral(_) => Ok(Type::Bool),
|
||||||
Identifier(name) => symbols.get(name).clone(),
|
Identifier(name) => Ok(symbols.get(name)?.clone()),
|
||||||
UnaryOp(op, ref mut expr) => {
|
UnaryOp(op, ref mut expr) => {
|
||||||
let expr_types = vec![type_check(expr, symbols)];
|
let expr_types = vec![type_check(expr, symbols)?];
|
||||||
|
|
||||||
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(&format!("unary_{op}"))
|
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(&format!("unary_{op}"))?
|
||||||
else {
|
else {
|
||||||
panic!("Identifier {} does not correspond to an operator!", op);
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: format!("Identifier {} does not correspond to an operator!", op),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
if expr_types != *sig_arg_types {
|
if expr_types != *sig_arg_types {
|
||||||
panic!(
|
return Err(Box::new(TypeCheckerError {
|
||||||
"Operator {} argument types {:?} don't match expected {:?}",
|
msg: format!(
|
||||||
op, expr_types, *sig_arg_types
|
"Operator {} argument types {:?} don't match expected {:?}",
|
||||||
);
|
op, expr_types, *sig_arg_types
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
(**sig_ret_type).clone()
|
Ok((**sig_ret_type).clone())
|
||||||
}
|
}
|
||||||
BinaryOp(ref mut left, op, ref mut right) => match *op {
|
BinaryOp(ref mut left, op, ref mut right) => match *op {
|
||||||
"==" | "!=" => {
|
"==" | "!=" => {
|
||||||
let left_type = type_check(left, symbols);
|
let left_type = type_check(left, symbols)?;
|
||||||
let right_type = type_check(right, symbols);
|
let right_type = type_check(right, symbols)?;
|
||||||
if left_type != right_type {
|
if left_type != right_type {
|
||||||
panic!("Mismatched types being compared with {op}");
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: format!("Mismatched types being compared with {op}"),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
Type::Bool
|
Ok(Type::Bool)
|
||||||
}
|
}
|
||||||
"=" => {
|
"=" => {
|
||||||
if !matches!(left.expr, Identifier(_)) {
|
if !matches!(left.expr, Identifier(_)) {
|
||||||
panic!("Non-variable on left side of assignment!");
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: String::from("Non-variable on left side of assignment!"),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let left_type = type_check(left, symbols);
|
let left_type = type_check(left, symbols)?;
|
||||||
let right_type = type_check(right, symbols);
|
let right_type = type_check(right, symbols)?;
|
||||||
if left_type != right_type {
|
if left_type != right_type {
|
||||||
panic!("Mismatched types in assignment!");
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: String::from("Mismatched types in assignment!"),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
left_type
|
Ok(left_type)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let left_type = type_check(left, symbols);
|
let left_type = type_check(left, symbols)?;
|
||||||
let right_type = type_check(right, symbols);
|
let right_type = type_check(right, symbols)?;
|
||||||
let arg_types = vec![left_type, right_type];
|
let arg_types = vec![left_type, right_type];
|
||||||
|
|
||||||
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(op) else {
|
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(op).unwrap() else {
|
||||||
panic!("Identifier {} does not correspond to an operator!", op);
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: format!("Identifier {} does not correspond to an operator!", op),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
if arg_types != *sig_arg_types {
|
if arg_types != *sig_arg_types {
|
||||||
panic!(
|
return Err(Box::new(TypeCheckerError {
|
||||||
"Operator {} argument types {:?} don't match expected {:?}",
|
msg: format!(
|
||||||
op, arg_types, *sig_arg_types
|
"Operator {} argument types {:?} don't match expected {:?}",
|
||||||
);
|
op, arg_types, *sig_arg_types
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
(**sig_ret_type).clone()
|
Ok((**sig_ret_type).clone())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
VarDeclaration(name, ref mut expr, ref mut type_expr) => {
|
VarDeclaration(name, ref mut expr, ref mut type_expr) => {
|
||||||
let type_var = type_check(expr, symbols);
|
let type_var = type_check(expr, symbols)?;
|
||||||
|
|
||||||
if let Some(type_expr) = type_expr {
|
if let Some(type_expr) = type_expr {
|
||||||
let expected_type = match type_expr {
|
let expected_type = match type_expr {
|
||||||
|
@ -86,69 +118,81 @@ fn get_type<'source>(ast: &mut AstNode<'source>, symbols: &mut SymTab<'source, T
|
||||||
};
|
};
|
||||||
|
|
||||||
if type_var != expected_type {
|
if type_var != expected_type {
|
||||||
panic!(
|
return Err(Box::new(TypeCheckerError {
|
||||||
"Expected type {:?} does not match actual type {:?} in var declaration",
|
msg: format!(
|
||||||
expected_type, type_var
|
"Expected type {:?} does not match actual type {:?} in var declaration",
|
||||||
)
|
expected_type, type_var
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
symbols.insert(name, type_var);
|
symbols.insert(name, type_var)?;
|
||||||
Type::Unit
|
Ok(Type::Unit)
|
||||||
}
|
}
|
||||||
Conditional(ref mut condition_expr, ref mut then_expr, ref mut else_expr) => {
|
Conditional(ref mut condition_expr, ref mut then_expr, ref mut else_expr) => {
|
||||||
if !matches!(type_check(condition_expr, symbols), Type::Bool) {
|
if !matches!(type_check(condition_expr, symbols)?, Type::Bool) {
|
||||||
panic!("Non-bool as if-then-else condition!");
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: String::from("Non-bool as if-then-else condition!"),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut else_expr) = else_expr {
|
if let Some(ref mut else_expr) = else_expr {
|
||||||
let then_type = type_check(then_expr, symbols);
|
let then_type = type_check(then_expr, symbols)?;
|
||||||
let else_type = type_check(else_expr, symbols);
|
let else_type = type_check(else_expr, symbols)?;
|
||||||
if then_type == else_type {
|
if then_type == else_type {
|
||||||
then_type
|
Ok(then_type)
|
||||||
} else {
|
} else {
|
||||||
panic!("Mismatched return values in if-then-else!");
|
Err(Box::new(TypeCheckerError {
|
||||||
|
msg: String::from("Mismatched return types in if-then-else!"),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Type::Unit
|
Ok(Type::Unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
While(ref mut condition_expr, ref mut do_expr) => {
|
While(ref mut condition_expr, ref mut do_expr) => {
|
||||||
if !matches!(type_check(condition_expr, symbols), Type::Bool) {
|
if !matches!(type_check(condition_expr, symbols)?, Type::Bool) {
|
||||||
panic!("Non-bool as while-do condition!");
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: String::from("Non-bool as while-do condition!"),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
type_check(do_expr, symbols);
|
type_check(do_expr, symbols)?;
|
||||||
Type::Unit
|
Ok(Type::Unit)
|
||||||
}
|
}
|
||||||
FunCall(name, args) => {
|
FunCall(name, args) => {
|
||||||
let mut arg_types = Vec::new();
|
let mut arg_types = Vec::new();
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg_types.push(type_check(arg, symbols));
|
arg_types.push(type_check(arg, symbols)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(name) else {
|
let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(name).unwrap() else {
|
||||||
panic!("Identifier {} does not correspond to a function!", name);
|
return Err(Box::new(TypeCheckerError {
|
||||||
|
msg: format!("Identifier {} does not correspond to a function!", name),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
if arg_types != *sig_arg_types {
|
if arg_types != *sig_arg_types {
|
||||||
panic!(
|
return Err(Box::new(TypeCheckerError {
|
||||||
"Function {} argument types {:?} don't match expected {:?}",
|
msg: format!(
|
||||||
name, arg_types, *sig_arg_types
|
"Function {} argument types {:?} don't match expected {:?}",
|
||||||
);
|
name, arg_types, *sig_arg_types
|
||||||
|
),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
(**sig_ret_type).clone()
|
Ok((**sig_ret_type).clone())
|
||||||
}
|
}
|
||||||
Block(ref mut expressions) => {
|
Block(ref mut expressions) => {
|
||||||
symbols.push_level();
|
symbols.push_level();
|
||||||
|
|
||||||
let mut type_var = Type::Unit;
|
let mut type_var = Type::Unit;
|
||||||
for expression in expressions {
|
for expression in expressions {
|
||||||
type_var = type_check(expression, symbols);
|
type_var = type_check(expression, symbols)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
symbols.remove_level();
|
symbols.remove_level();
|
||||||
type_var
|
Ok(type_var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +208,7 @@ mod tests {
|
||||||
&mut parse(&tokenize(code).unwrap()).unwrap(),
|
&mut parse(&tokenize(code).unwrap()).unwrap(),
|
||||||
&mut SymTab::new_type_table(),
|
&mut SymTab::new_type_table(),
|
||||||
)
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -323,15 +368,19 @@ mod tests {
|
||||||
let mut tokens = tokenize("foo(1)").unwrap();
|
let mut tokens = tokenize("foo(1)").unwrap();
|
||||||
let mut ast = parse(&tokens).unwrap();
|
let mut ast = parse(&tokens).unwrap();
|
||||||
let mut symtab = SymTab::new_type_table();
|
let mut symtab = SymTab::new_type_table();
|
||||||
symtab.insert("foo", Func(vec![Int], Box::new(Int)));
|
symtab
|
||||||
let result = type_check(&mut ast, &mut symtab);
|
.insert("foo", Func(vec![Int], Box::new(Int)))
|
||||||
|
.unwrap();
|
||||||
|
let result = type_check(&mut ast, &mut symtab).unwrap();
|
||||||
assert_eq!(result, Int);
|
assert_eq!(result, Int);
|
||||||
|
|
||||||
tokens = tokenize("foo(1);").unwrap();
|
tokens = tokenize("foo(1);").unwrap();
|
||||||
ast = parse(&tokens).unwrap();
|
ast = parse(&tokens).unwrap();
|
||||||
symtab = SymTab::new_type_table();
|
symtab = SymTab::new_type_table();
|
||||||
symtab.insert("foo", Func(vec![Int], Box::new(Int)));
|
symtab
|
||||||
let result = type_check(&mut ast, &mut symtab);
|
.insert("foo", Func(vec![Int], Box::new(Int)))
|
||||||
|
.unwrap();
|
||||||
|
let result = type_check(&mut ast, &mut symtab).unwrap();
|
||||||
assert_eq!(result, Unit);
|
assert_eq!(result, Unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,8 +390,10 @@ mod tests {
|
||||||
let tokens = tokenize("foo(true)").unwrap();
|
let tokens = tokenize("foo(true)").unwrap();
|
||||||
let mut ast = parse(&tokens).unwrap();
|
let mut ast = parse(&tokens).unwrap();
|
||||||
let mut symtab = SymTab::new_type_table();
|
let mut symtab = SymTab::new_type_table();
|
||||||
symtab.insert("foo", Func(vec![Int], Box::new(Int)));
|
symtab
|
||||||
type_check(&mut ast, &mut symtab);
|
.insert("foo", Func(vec![Int], Box::new(Int)))
|
||||||
|
.unwrap();
|
||||||
|
type_check(&mut ast, &mut symtab).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -352,7 +403,7 @@ mod tests {
|
||||||
let mut symtab = SymTab::new_type_table();
|
let mut symtab = SymTab::new_type_table();
|
||||||
|
|
||||||
assert_eq!(ast.node_type, Unit);
|
assert_eq!(ast.node_type, Unit);
|
||||||
type_check(&mut ast, &mut symtab);
|
type_check(&mut ast, &mut symtab).unwrap();
|
||||||
assert_eq!(ast.node_type, Int);
|
assert_eq!(ast.node_type, Int);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue