1
0
Fork 0

Finalize error handling

This commit is contained in:
Vili Sinervä 2025-02-26 23:30:04 +02:00
parent 3e4d3fad7d
commit e67eeaaa81
No known key found for this signature in database
GPG key ID: DF8FEAF54EFAC996
5 changed files with 165 additions and 95 deletions

View file

@ -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}")))
),
} }
} }
} }

View file

@ -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);
}; };

View file

@ -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!");
}; };

View file

@ -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(())
} }
} }
} }

View file

@ -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);
} }
} }