From e67eeaaa8166a9164157ab38f92c8dd9d681b362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vili=20Sinerv=C3=A4?= Date: Wed, 26 Feb 2025 23:30:04 +0200 Subject: [PATCH] Finalize error handling --- src/compiler.rs | 7 +- src/compiler/interpreter.rs | 15 +-- src/compiler/ir_generator.rs | 18 ++-- src/compiler/symtab.rs | 31 ++++-- src/compiler/type_checker.rs | 189 ++++++++++++++++++++++------------- 5 files changed, 165 insertions(+), 95 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index d2a7716..dc052fc 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -26,7 +26,7 @@ mod variable; pub fn compile(code: &str) -> Result> { let tokens = tokenize(code)?; 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 assembly = generate_assembly(&ir); @@ -38,10 +38,7 @@ pub fn start_compiler() { for line in lines.map_while(Result::ok) { match compile(&line) { Ok(_) => println!("\nCompilation OK :)\n"), - Err(e) => println!( - "{}", - format!("{{\"error\": {}}}", json::stringify(format!("{e}"))) - ), + Err(e) => println!("\n{e}\n"), } } } diff --git a/src/compiler/interpreter.rs b/src/compiler/interpreter.rs index d9885e3..f08b39f 100644 --- a/src/compiler/interpreter.rs +++ b/src/compiler/interpreter.rs @@ -7,14 +7,17 @@ use crate::compiler::{ 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 { match &ast.expr { EmptyLiteral() => Value::None(), IntLiteral(val) => Value::Int(*val), BoolLiteral(val) => Value::Bool(*val), - Identifier(name) => *symbols.get(name), + Identifier(name) => *symbols.get(name).unwrap(), 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); }; 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 { let val = interpret(right, symbols); - *symbols.get(name) = val; + *symbols.get(name).unwrap() = val; val } else { 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); }; 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, _) => { let val = interpret(expr, symbols); - symbols.insert(name, val); + symbols.insert(name, val).unwrap(); Value::None() } 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)); } - 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); }; diff --git a/src/compiler/ir_generator.rs b/src/compiler/ir_generator.rs index fdccb35..c1cbea3 100644 --- a/src/compiler/ir_generator.rs +++ b/src/compiler/ir_generator.rs @@ -20,7 +20,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec { let global_types = IrVar::new_global_types(); let mut types = global_types.clone(); for var in global_types.keys() { - symbols.insert(&var.name, var.clone()); + symbols.insert(&var.name, var.clone()).unwrap(); } let result = visit_ast_node( @@ -34,7 +34,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec { match types.get(&result) { Some(Type::Int) => { 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); instructions.push(IrInstruction::new( @@ -44,7 +44,7 @@ pub fn generate_ir(ast: &AstNode) -> Vec { } Some(Type::Bool) => { 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); instructions.push(IrInstruction::new( @@ -121,9 +121,9 @@ fn visit_ast_node<'source>( }); var } - Identifier(name) => symbols.get(name).clone(), + Identifier(name) => symbols.get(name).unwrap().clone(), 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 result_var = add_var(&ast.node_type, types); @@ -140,7 +140,7 @@ fn visit_ast_node<'source>( let Identifier(var_name) = left.expr else { 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()))); @@ -215,7 +215,7 @@ fn visit_ast_node<'source>( 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 right_var = visit_ast_node(right, types, symbols, instructions, labels); let result_var = add_var(&ast.node_type, types); @@ -231,7 +231,7 @@ fn visit_ast_node<'source>( VarDeclaration(name, expr, _) => { let expr_var = visit_ast_node(expr, types, symbols, instructions, labels); 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))); add_var(&Type::Unit, types) } @@ -307,7 +307,7 @@ fn visit_ast_node<'source>( add_var(&Type::Unit, types) } 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 { panic!("Function call does not have entry in types table!"); }; diff --git a/src/compiler/symtab.rs b/src/compiler/symtab.rs index 68fd192..19e9b2a 100644 --- a/src/compiler/symtab.rs +++ b/src/compiler/symtab.rs @@ -1,5 +1,18 @@ 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)] pub struct SymTab<'source, T> { @@ -7,13 +20,15 @@ pub struct 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() { 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) { @@ -24,7 +39,7 @@ impl<'source, T> SymTab<'source, T> { 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 .tables .last_mut() @@ -32,7 +47,11 @@ impl<'source, T> SymTab<'source, T> { .insert(name, val) .is_some() { - panic!("Variable {} already defined in this scope!", name) + Err(SymbolTableError { + msg: format!("Variable {} already defined in this scope!", name), + }) + } else { + Ok(()) } } } diff --git a/src/compiler/type_checker.rs b/src/compiler/type_checker.rs index 8cf9568..99e453f 100644 --- a/src/compiler/type_checker.rs +++ b/src/compiler/type_checker.rs @@ -1,83 +1,115 @@ +use std::{error::Error, fmt::Display}; + use crate::compiler::{ ast::{AstNode, Expression::*, TypeExpression}, symtab::SymTab, 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>( ast: &mut AstNode<'source>, symbols: &mut SymTab<'source, Type>, -) -> Type { - let node_type = get_type(ast, symbols); +) -> Result> { + let node_type = get_type(ast, symbols)?; 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> { match &mut ast.expr { - EmptyLiteral() => Type::Unit, - IntLiteral(_) => Type::Int, - BoolLiteral(_) => Type::Bool, - Identifier(name) => symbols.get(name).clone(), + EmptyLiteral() => Ok(Type::Unit), + IntLiteral(_) => Ok(Type::Int), + BoolLiteral(_) => Ok(Type::Bool), + Identifier(name) => Ok(symbols.get(name)?.clone()), 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 { - 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 { - panic!( - "Operator {} argument types {:?} don't match expected {:?}", - op, expr_types, *sig_arg_types - ); + return Err(Box::new(TypeCheckerError { + msg: format!( + "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 { "==" | "!=" => { - let left_type = type_check(left, symbols); - let right_type = type_check(right, symbols); + let left_type = type_check(left, symbols)?; + let right_type = type_check(right, symbols)?; 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(_)) { - 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 right_type = type_check(right, symbols); + let left_type = type_check(left, symbols)?; + let right_type = type_check(right, symbols)?; 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 right_type = type_check(right, symbols); + let left_type = type_check(left, symbols)?; + let right_type = type_check(right, symbols)?; let arg_types = vec![left_type, right_type]; - let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(op) else { - panic!("Identifier {} does not correspond to an operator!", op); + let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(op).unwrap() else { + return Err(Box::new(TypeCheckerError { + msg: format!("Identifier {} does not correspond to an operator!", op), + })); }; if arg_types != *sig_arg_types { - panic!( - "Operator {} argument types {:?} don't match expected {:?}", - op, arg_types, *sig_arg_types - ); + return Err(Box::new(TypeCheckerError { + msg: format!( + "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) => { - let type_var = type_check(expr, symbols); + let type_var = type_check(expr, symbols)?; if let Some(type_expr) = 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 { - panic!( - "Expected type {:?} does not match actual type {:?} in var declaration", - expected_type, type_var - ) + return Err(Box::new(TypeCheckerError { + msg: format!( + "Expected type {:?} does not match actual type {:?} in var declaration", + expected_type, type_var + ), + })); } } - symbols.insert(name, type_var); - Type::Unit + symbols.insert(name, type_var)?; + Ok(Type::Unit) } Conditional(ref mut condition_expr, ref mut then_expr, ref mut else_expr) => { - if !matches!(type_check(condition_expr, symbols), Type::Bool) { - panic!("Non-bool as if-then-else condition!"); + if !matches!(type_check(condition_expr, symbols)?, Type::Bool) { + return Err(Box::new(TypeCheckerError { + msg: String::from("Non-bool as if-then-else condition!"), + })); } if let Some(ref mut else_expr) = else_expr { - let then_type = type_check(then_expr, symbols); - let else_type = type_check(else_expr, symbols); + let then_type = type_check(then_expr, symbols)?; + let else_type = type_check(else_expr, symbols)?; if then_type == else_type { - then_type + Ok(then_type) } else { - panic!("Mismatched return values in if-then-else!"); + Err(Box::new(TypeCheckerError { + msg: String::from("Mismatched return types in if-then-else!"), + })) } } else { - Type::Unit + Ok(Type::Unit) } } While(ref mut condition_expr, ref mut do_expr) => { - if !matches!(type_check(condition_expr, symbols), Type::Bool) { - panic!("Non-bool as while-do condition!"); + if !matches!(type_check(condition_expr, symbols)?, Type::Bool) { + return Err(Box::new(TypeCheckerError { + msg: String::from("Non-bool as while-do condition!"), + })); } - type_check(do_expr, symbols); - Type::Unit + type_check(do_expr, symbols)?; + Ok(Type::Unit) } FunCall(name, args) => { let mut arg_types = Vec::new(); 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 { - panic!("Identifier {} does not correspond to a function!", name); + let Type::Func(sig_arg_types, sig_ret_type) = symbols.get(name).unwrap() else { + return Err(Box::new(TypeCheckerError { + msg: format!("Identifier {} does not correspond to a function!", name), + })); }; if arg_types != *sig_arg_types { - panic!( - "Function {} argument types {:?} don't match expected {:?}", - name, arg_types, *sig_arg_types - ); + return Err(Box::new(TypeCheckerError { + msg: format!( + "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) => { symbols.push_level(); let mut type_var = Type::Unit; for expression in expressions { - type_var = type_check(expression, symbols); + type_var = type_check(expression, symbols)?; } symbols.remove_level(); - type_var + Ok(type_var) } } } @@ -164,6 +208,7 @@ mod tests { &mut parse(&tokenize(code).unwrap()).unwrap(), &mut SymTab::new_type_table(), ) + .unwrap() } #[test] @@ -323,15 +368,19 @@ mod tests { let mut tokens = tokenize("foo(1)").unwrap(); let mut ast = parse(&tokens).unwrap(); let mut symtab = SymTab::new_type_table(); - symtab.insert("foo", Func(vec![Int], Box::new(Int))); - let result = type_check(&mut ast, &mut symtab); + symtab + .insert("foo", Func(vec![Int], Box::new(Int))) + .unwrap(); + let result = type_check(&mut ast, &mut symtab).unwrap(); assert_eq!(result, Int); tokens = tokenize("foo(1);").unwrap(); ast = parse(&tokens).unwrap(); symtab = SymTab::new_type_table(); - symtab.insert("foo", Func(vec![Int], Box::new(Int))); - let result = type_check(&mut ast, &mut symtab); + symtab + .insert("foo", Func(vec![Int], Box::new(Int))) + .unwrap(); + let result = type_check(&mut ast, &mut symtab).unwrap(); assert_eq!(result, Unit); } @@ -341,8 +390,10 @@ mod tests { let tokens = tokenize("foo(true)").unwrap(); let mut ast = parse(&tokens).unwrap(); let mut symtab = SymTab::new_type_table(); - symtab.insert("foo", Func(vec![Int], Box::new(Int))); - type_check(&mut ast, &mut symtab); + symtab + .insert("foo", Func(vec![Int], Box::new(Int))) + .unwrap(); + type_check(&mut ast, &mut symtab).unwrap(); } #[test] @@ -352,7 +403,7 @@ mod tests { let mut symtab = SymTab::new_type_table(); 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); } }