From e35bacb5830951a2e16d8e7d8329f496537e3008 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Mon, 9 Mar 2026 16:35:19 -0500 Subject: [PATCH] Bunch of AST refactoring to make api easier. --- dm/src/main.rs | 74 +++++++----- ...ditive_expression.rs => add_expression.rs} | 35 +++--- dmc-lib/src/ast/call.rs | 102 +++++++++++------ dmc-lib/src/ast/compilation_unit.rs | 59 +++++++--- dmc-lib/src/ast/expression.rs | 108 +++++++++--------- dmc-lib/src/ast/expression_statement.rs | 9 +- dmc-lib/src/ast/extern_function.rs | 37 ++++-- dmc-lib/src/ast/function.rs | 67 ++++++++--- dmc-lib/src/ast/identifier.rs | 45 ++++---- dmc-lib/src/ast/integer_literal.rs | 8 ++ dmc-lib/src/ast/let_statement.rs | 39 ++++--- dmc-lib/src/ast/mod.rs | 2 +- dmc-lib/src/ast/module_level_declaration.rs | 9 +- dmc-lib/src/ast/negative_expression.rs | 36 ++++++ dmc-lib/src/ast/statement.rs | 9 +- dmc-lib/src/ast/string_literal.rs | 8 ++ dmc-lib/src/ast/subtract_expression.rs | 61 ++++++++++ dmc-lib/src/ir/ir_function.rs | 4 +- dmc-lib/src/ir/ir_variable.rs | 6 +- dmc-lib/src/parser.rs | 19 ++- dmc-lib/src/symbol.rs | 18 ++- dmc-lib/src/type_info.rs | 27 ++++- e2e-tests/src/lib.rs | 24 ++-- 23 files changed, 561 insertions(+), 245 deletions(-) rename dmc-lib/src/ast/{additive_expression.rs => add_expression.rs} (69%) diff --git a/dm/src/main.rs b/dm/src/main.rs index 2bcb942..ddd1dab 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -47,14 +47,26 @@ fn main() { let mut symbol_table = SymbolTable::new(); - let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table); - check_and_report_diagnostics(&files, script_file_id, &gather_names_diagnostics); + match compilation_unit.gather_declared_names(&mut symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_and_exit(&diagnostics, script_file_id, &files); + } + } - let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table); - check_and_report_diagnostics(&files, script_file_id, &name_usages_diagnostics); + match compilation_unit.check_name_usages(&symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_and_exit(&diagnostics, script_file_id, &files); + } + } - let type_check_diagnostics = compilation_unit.type_check(&symbol_table); - check_and_report_diagnostics(&files, script_file_id, &type_check_diagnostics); + match compilation_unit.type_check(&symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_and_exit(&diagnostics, script_file_id, &files); + } + } let mut ir_functions = compilation_unit.to_ir(&symbol_table); @@ -118,26 +130,34 @@ fn check_and_report_diagnostics( diagnostics: &[Diagnostic], ) { if !diagnostics.is_empty() { - let writer = StandardStream::stderr(ColorChoice::Always); - let config = term::Config::default(); - for diagnostic in diagnostics { - let csr_diagnostic = codespan_reporting::diagnostic::Diagnostic::error() - .with_message(diagnostic.message()) - .with_label(Label::primary( - script_file_id, - diagnostic.start()..diagnostic.end(), - )); - - term::emit_to_write_style(&mut writer.lock(), &config, files, &csr_diagnostic).unwrap(); - } - if diagnostics.len() == 1 { - eprintln!("There was 1 diagnostic. See above for more information."); - } else { - eprintln!( - "There were {} diagnostics. See above for more information.", - diagnostics.len() - ); - } - std::process::exit(1); + report_and_exit(diagnostics, script_file_id, files); } } + +fn report_and_exit( + diagnostics: &[Diagnostic], + script_file_id: usize, + files: &SimpleFiles<&str, &str>, +) -> ! { + let writer = StandardStream::stderr(ColorChoice::Always); + let config = term::Config::default(); + for diagnostic in diagnostics { + let csr_diagnostic = codespan_reporting::diagnostic::Diagnostic::error() + .with_message(diagnostic.message()) + .with_label(Label::primary( + script_file_id, + diagnostic.start()..diagnostic.end(), + )); + + term::emit_to_write_style(&mut writer.lock(), &config, files, &csr_diagnostic).unwrap(); + } + if diagnostics.len() == 1 { + eprintln!("There was 1 diagnostic. See above for more information."); + } else { + eprintln!( + "There were {} diagnostics. See above for more information.", + diagnostics.len() + ); + } + std::process::exit(1); +} diff --git a/dmc-lib/src/ast/additive_expression.rs b/dmc-lib/src/ast/add_expression.rs similarity index 69% rename from dmc-lib/src/ast/additive_expression.rs rename to dmc-lib/src/ast/add_expression.rs index 6e2773c..98d5ee2 100644 --- a/dmc-lib/src/ast/additive_expression.rs +++ b/dmc-lib/src/ast/add_expression.rs @@ -6,18 +6,20 @@ use crate::source_range::SourceRange; use crate::symbol_table::SymbolTable; use crate::type_info::TypeInfo; -pub struct AdditiveExpression { +pub struct AddExpression { lhs: Box, rhs: Box, source_range: SourceRange, + type_info: Option, } -impl AdditiveExpression { +impl AddExpression { pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self { Self { lhs: lhs.into(), rhs: rhs.into(), source_range, + type_info: None, } } @@ -33,9 +35,12 @@ impl AdditiveExpression { &mut self, symbol_table: &mut SymbolTable, ) -> Result<(), Vec> { - let mut diagnostics = vec![]; - diagnostics.append(&mut self.lhs.gather_declared_names(symbol_table)); - diagnostics.append(&mut self.rhs.gather_declared_names(symbol_table)); + let diagnostics: Vec = [self.lhs.as_mut(), self.rhs.as_mut()] + .iter_mut() + .map(|expression| expression.gather_declared_names(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); if diagnostics.is_empty() { Ok(()) } else { @@ -44,9 +49,12 @@ impl AdditiveExpression { } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - let mut diagnostics = vec![]; - diagnostics.append(&mut self.lhs.check_name_usages(symbol_table)); - diagnostics.append(&mut self.rhs.check_name_usages(symbol_table)); + let diagnostics: Vec = [self.lhs.as_mut(), self.rhs.as_mut()] + .iter_mut() + .map(|expression| expression.check_name_usages(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); if diagnostics.is_empty() { Ok(()) } else { @@ -55,12 +63,13 @@ impl AdditiveExpression { } pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { - self.lhs.type_check(symbol_table); - self.rhs.type_check(symbol_table); + self.lhs.type_check(symbol_table)?; + self.rhs.type_check(symbol_table)?; let lhs_type_info = self.lhs.type_info(); let rhs_type_info = self.rhs.type_info(); - if lhs_type_info.can_add(&rhs_type_info) { + if lhs_type_info.can_add(rhs_type_info) { + self.type_info = Some(lhs_type_info.add_result(rhs_type_info)); Ok(()) } else { Err(vec![Diagnostic::new( @@ -83,8 +92,8 @@ impl AdditiveExpression { IrAdd::new(lhs_ir_expression, rhs_ir_expression) } - pub fn type_info(&self) -> TypeInfo { - self.lhs.type_info().additive_result(&self.rhs.type_info()) + pub fn type_info(&self) -> &TypeInfo { + self.type_info.as_ref().unwrap() } pub fn source_range(&self) -> &SourceRange { diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index 1b88cbd..08e9971 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -14,6 +14,7 @@ pub struct Call { callee: Box, arguments: Vec, source_range: SourceRange, + return_type_info: Option, } impl Call { @@ -22,6 +23,7 @@ impl Call { callee: callee.into(), arguments, source_range, + return_type_info: None, } } @@ -33,44 +35,74 @@ impl Call { self.arguments.iter().collect() } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { - let mut diagnostics = vec![]; - diagnostics.append(&mut self.callee.gather_declared_names(symbol_table)); - for argument in &mut self.arguments { - diagnostics.append(&mut argument.gather_declared_names(symbol_table)); + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + let mut to_gather = vec![]; + to_gather.push(self.callee.as_mut()); + to_gather.extend(&mut self.arguments); + + let diagnostics: Vec = to_gather + .iter_mut() + .map(|expression| expression.gather_declared_names(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) } - diagnostics } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { - let mut diagnostics = vec![]; - diagnostics.append(&mut self.callee.check_name_usages(symbol_table)); - for argument in &mut self.arguments { - diagnostics.append(&mut argument.check_name_usages(symbol_table)); + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let mut to_check = vec![]; + to_check.push(self.callee.as_mut()); + to_check.extend(&mut self.arguments); + let diagnostics: Vec = to_check + .iter_mut() + .map(|expression| expression.check_name_usages(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) } - diagnostics } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { - let mut diagnostics = vec![]; - diagnostics.append(&mut self.callee.type_check(symbol_table)); - for argument in &mut self.arguments { - diagnostics.append(&mut argument.type_check(symbol_table)); - } + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.callee.as_mut().type_check(symbol_table)?; + + let mut diagnostics: Vec = self + .arguments + .iter_mut() + .map(|argument| argument.type_check(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); // check that callee is callable let function_symbol = match self.callee.type_info() { TypeInfo::Function(function_symbol) => function_symbol, _ => { diagnostics.push(Diagnostic::new( - "Receiver is not callable", + &format!( + "Receiver of type {} is not callable.", + self.callee.type_info() + ), self.callee.source_range().start(), self.callee.source_range().end(), )); - return diagnostics; + return Err(diagnostics); } }; + // set return type + self.return_type_info = Some(function_symbol.borrow().return_type_info().clone()); + // check arguments length let function_symbol_ref = function_symbol.borrow(); let parameters = function_symbol_ref.parameters(); @@ -87,23 +119,20 @@ impl Call { } if !diagnostics.is_empty() { - return diagnostics; + return Err(diagnostics); } // check argument types for i in 0..parameters.len() { - let parameter = ¶meters[i]; + let parameter = ¶meters[i].borrow(); let argument = &self.arguments[i]; - if !parameter - .borrow() - .type_info() - .is_assignable_from(&argument.type_info()) - { + let parameter_type_info = parameter.type_info(); + let argument_type_info = argument.type_info(); + if !parameter_type_info.is_assignable_from(argument_type_info) { diagnostics.push(Diagnostic::new( &format!( - "Mismatched types; expected {} but found {}", - parameter.borrow().type_info(), - argument.type_info() + "Mismatched types: expected {} but found {}", + parameter_type_info, argument_type_info ), argument.source_range().start(), argument.source_range().end(), @@ -111,14 +140,15 @@ impl Call { } } - diagnostics + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn type_info(&self) -> TypeInfo { - match self.callee.type_info() { - TypeInfo::Function(function_symbol) => function_symbol.borrow().return_type(), - _ => panic!(), - } + pub fn return_type_info(&self) -> &TypeInfo { + self.return_type_info.as_ref().unwrap() } fn get_callee_symbol(&self) -> Rc> { diff --git a/dmc-lib/src/ast/compilation_unit.rs b/dmc-lib/src/ast/compilation_unit.rs index 1c8aaa1..f65db1f 100644 --- a/dmc-lib/src/ast/compilation_unit.rs +++ b/dmc-lib/src/ast/compilation_unit.rs @@ -16,30 +16,57 @@ impl CompilationUnit { &self.declarations } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { - let mut diagnostics = vec![]; + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { symbol_table.push_scope("compilation_unit_scope"); - for declaration in &mut self.declarations { - diagnostics.append(&mut declaration.gather_declared_names(symbol_table)); - } + + let diagnostics: Vec = self + .declarations + .iter_mut() + .map(|declaration| declaration.gather_declared_names(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + symbol_table.pop_scope(); - diagnostics + + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { - let mut diagnostics = vec![]; - for declaration in &mut self.declarations { - diagnostics.append(&mut declaration.check_name_usages(symbol_table)); + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let diagnostics: Vec = self + .declarations + .iter_mut() + .map(|declaration| declaration.check_name_usages(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) } - diagnostics } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { - let mut diagnostics = vec![]; - for declaration in &mut self.declarations { - diagnostics.append(&mut declaration.type_check(symbol_table)); + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let diagnostics: Vec = self + .declarations + .iter_mut() + .map(|declaration| declaration.type_check(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) } - diagnostics } pub fn to_ir(&self, symbol_table: &SymbolTable) -> Vec { diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index c8ee0ff..73b489c 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -1,4 +1,4 @@ -use crate::ast::additive_expression::AdditiveExpression; +use crate::ast::add_expression::AddExpression; use crate::ast::call::Call; use crate::ast::identifier::Identifier; use crate::ast::integer_literal::IntegerLiteral; @@ -19,84 +19,88 @@ use std::cell::RefCell; use std::rc::Rc; pub enum Expression { - Call(Call), - IntegerLiteral(IntegerLiteral), - String(StringLiteral), - Identifier(Identifier), - Additive(AdditiveExpression), + Add(AddExpression), Subtract(SubtractExpression), Negative(NegativeExpression), + Call(Call), + Identifier(Identifier), + Integer(IntegerLiteral), + String(StringLiteral), } impl Expression { - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { match self { + Expression::Add(add_expression) => add_expression.gather_declared_names(symbol_table), + Expression::Subtract(subtract_expression) => { + subtract_expression.gather_declared_names(symbol_table) + } + Expression::Negative(negative_expression) => { + negative_expression.gather_declared_names(symbol_table) + } Expression::Call(call) => call.gather_declared_names(symbol_table), Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), - Expression::Additive(additive_expression) => { - match additive_expression.gather_declared_names(symbol_table) { - Ok(_) => { - vec![] - } - Err(diagnostics) => diagnostics, - } - } - _ => vec![], + Expression::Integer(_) => Ok(()), + Expression::String(_) => Ok(()), } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { + Expression::Add(add_expression) => add_expression.check_name_usages(symbol_table), + Expression::Subtract(subtract_expression) => { + subtract_expression.check_name_usages(symbol_table) + } + Expression::Negative(negative_expression) => { + negative_expression.check_name_usages(symbol_table) + } Expression::Call(call) => call.check_name_usages(symbol_table), Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table), - Expression::Additive(additive_expression) => { - match additive_expression.check_name_usages(symbol_table) { - Ok(_) => { - vec![] - } - Err(diagnostics) => diagnostics, - } - } - _ => vec![], + Expression::Integer(_) => Ok(()), + Expression::String(_) => Ok(()), } } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { + Expression::Add(add_expression) => add_expression.type_check(symbol_table), + Expression::Subtract(subtract_expression) => { + subtract_expression.type_check(symbol_table) + } + Expression::Negative(negative_expression) => { + negative_expression.type_check(symbol_table) + } Expression::Call(call) => call.type_check(symbol_table), - Expression::Additive(additive_expression) => { - match additive_expression.type_check(symbol_table) { - Ok(_) => { - vec![] - } - Err(diagnostics) => diagnostics, - } - } - _ => vec![], + Expression::Identifier(identifier) => identifier.type_check(symbol_table), + Expression::Integer(_) => Ok(()), + Expression::String(_) => Ok(()), } } - pub fn type_info(&self) -> TypeInfo { + pub fn type_info(&self) -> &TypeInfo { match self { - Expression::Call(call) => call.type_info(), - Expression::IntegerLiteral(_) => TypeInfo::Integer, - Expression::String(_) => TypeInfo::String, + Expression::Add(add_expression) => add_expression.type_info(), + Expression::Subtract(subtract_expression) => subtract_expression.type_info(), + Expression::Negative(negative_expression) => negative_expression.type_info(), + Expression::Call(call) => call.return_type_info(), Expression::Identifier(identifier) => identifier.type_info(), - Expression::Additive(additive_expression) => additive_expression.type_info(), - Expression::Subtract(subtract_expression) => todo!(), - Expression::Negative(_) => todo!(), + Expression::Integer(integer_literal) => integer_literal.type_info(), + Expression::String(string_literal) => string_literal.type_info(), } } pub fn source_range(&self) -> &SourceRange { match self { - Expression::Call(call) => call.source_range(), - Expression::IntegerLiteral(integer_literal) => integer_literal.source_range(), - Expression::String(string_literal) => string_literal.source_range(), - Expression::Identifier(identifier) => identifier.source_range(), - Expression::Additive(additive_expression) => additive_expression.source_range(), + Expression::Add(additive_expression) => additive_expression.source_range(), Expression::Subtract(subtract_expression) => subtract_expression.source_range(), Expression::Negative(negative_expression) => negative_expression.source_range(), + Expression::Call(call) => call.source_range(), + Expression::Identifier(identifier) => identifier.source_range(), + Expression::Integer(integer_literal) => integer_literal.source_range(), + Expression::String(string_literal) => string_literal.source_range(), } } @@ -108,7 +112,7 @@ impl Expression { match self { Expression::Call(call) => { let ir_call = call.to_ir(builder, symbol_table); - if matches!(call.type_info(), TypeInfo::Void) { + if matches!(call.return_type_info(), TypeInfo::Void) { builder .current_block_mut() .add_statement(IrStatement::Call(ir_call)); @@ -117,7 +121,7 @@ impl Expression { let t_var = IrVariable::new_vr( builder.new_t_var().into(), builder.current_block().id(), - call.type_info(), + call.return_type_info(), ); let as_rc = Rc::new(RefCell::new(t_var)); let assign = IrAssign::new(as_rc.clone(), IrOperation::Call(ir_call)); @@ -127,7 +131,7 @@ impl Expression { Some(IrExpression::Variable(as_rc)) } } - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { Some(IrExpression::Int(integer_literal.value())) } Expression::String(string_literal) => { @@ -137,7 +141,7 @@ impl Expression { let expressible_symbol = identifier.expressible_symbol(); Some(expressible_symbol.ir_expression()) } - Expression::Additive(additive_expression) => { + Expression::Add(additive_expression) => { let ir_add = additive_expression.to_ir(builder, symbol_table); let t_var = IrVariable::new_vr( builder.new_t_var().into(), diff --git a/dmc-lib/src/ast/expression_statement.rs b/dmc-lib/src/ast/expression_statement.rs index a94d34e..d721fad 100644 --- a/dmc-lib/src/ast/expression_statement.rs +++ b/dmc-lib/src/ast/expression_statement.rs @@ -20,15 +20,18 @@ impl ExpressionStatement { &self.expression } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { self.expression.gather_declared_names(symbol_table) } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { self.expression.check_name_usages(symbol_table) } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { self.expression.type_check(symbol_table) } diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs index f52d00a..75d32c2 100644 --- a/dmc-lib/src/ast/extern_function.rs +++ b/dmc-lib/src/ast/extern_function.rs @@ -31,7 +31,10 @@ impl ExternFunction { &self.declared_name } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { let mut diagnostics = vec![]; let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( @@ -53,7 +56,7 @@ impl ExternFunction { self.declared_name_source_range.start(), self.declared_name_source_range.end(), )); - diagnostics + Err(diagnostics) } }; } @@ -79,24 +82,40 @@ impl ExternFunction { symbol_table.pop_scope(); - diagnostics + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { - self.parameters + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let diagnostics: Vec = self + .parameters .iter_mut() .map(|parameter| parameter.check_name_usages(symbol_table)) .filter_map(Result::err) .flatten() - .collect() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { - self.parameters + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let diagnostics: Vec = self + .parameters .iter_mut() .map(|parameter| parameter.type_check(symbol_table)) .filter_map(Result::err) .flatten() - .collect() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } } diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index a0f93bd..57b19f9 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -48,7 +48,10 @@ impl Function { self.statements.iter().collect() } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { let mut diagnostics = vec![]; // insert function symbol @@ -75,7 +78,7 @@ impl Function { self.declared_name_source_range.start(), self.declared_name_source_range.end(), )); - diagnostics + Err(diagnostics) } }; } @@ -103,14 +106,24 @@ impl Function { symbol_table.push_scope(&format!("body_scope({})", self.declared_name)); for statement in &mut self.statements { - diagnostics.append(&mut statement.gather_declared_names(symbol_table)); + match statement.gather_declared_names(symbol_table) { + Ok(_) => {} + Err(mut statement_diagnostics) => { + diagnostics.append(&mut statement_diagnostics); + } + } } symbol_table.pop_scope(); // body symbol_table.pop_scope(); // params - diagnostics + + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics = vec![]; // parameters @@ -126,12 +139,22 @@ impl Function { // statements for statement in &mut self.statements { - diagnostics.append(&mut statement.check_name_usages(symbol_table)); + match statement.check_name_usages(symbol_table) { + Ok(_) => {} + Err(mut statement_diagnostics) => { + diagnostics.append(&mut statement_diagnostics); + } + } + } + + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) } - diagnostics } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let mut diagnostics = vec![]; // parameters @@ -140,17 +163,27 @@ impl Function { .parameters .iter_mut() .map(|parameter| parameter.type_check(symbol_table)) - .filter_map(|result| result.err()) + .filter_map(Result::err) .flatten() .collect(), ); // statements - for statement in &mut self.statements { - diagnostics.append(&mut statement.type_check(symbol_table)); - } + diagnostics.append( + &mut self + .statements + .iter_mut() + .map(|statement| statement.type_check(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(), + ); - diagnostics + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } pub fn to_ir(&self, symbol_table: &SymbolTable) -> IrFunction { @@ -172,12 +205,8 @@ impl Function { let entry_block_id = builder.new_block(); - let return_type_info = self - .function_symbol - .as_ref() - .unwrap() - .borrow() - .return_type(); + let function_symbol = self.function_symbol.as_ref().unwrap().borrow(); + let return_type_info = function_symbol.return_type_info(); let should_return_value = !matches!(return_type_info, TypeInfo::Void); diff --git a/dmc-lib/src/ast/identifier.rs b/dmc-lib/src/ast/identifier.rs index 37e1cb7..85efe97 100644 --- a/dmc-lib/src/ast/identifier.rs +++ b/dmc-lib/src/ast/identifier.rs @@ -8,6 +8,7 @@ pub struct Identifier { name: String, scope_id: Option, expressible_symbol: Option, + type_info: Option, source_range: SourceRange, } @@ -17,6 +18,7 @@ impl Identifier { name: name.into(), scope_id: None, expressible_symbol: None, + type_info: None, source_range, } } @@ -25,41 +27,38 @@ impl Identifier { &self.name } - pub fn gather_declared_names(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &SymbolTable, + ) -> Result<(), Vec> { self.scope_id = Some(symbol_table.current_scope_id()); - vec![] + Ok(()) } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { let maybe_expressible_symbol = symbol_table.find_expressible_symbol(self.scope_id.unwrap(), &self.name); match maybe_expressible_symbol { - None => { - vec![Diagnostic::new( - &format!("Unable to resolve symbol {}", self.name), - self.source_range.start(), - self.source_range.end(), - )] - } + None => Err(vec![Diagnostic::new( + &format!("Unable to resolve symbol {}", self.name), + self.source_range.start(), + self.source_range.end(), + )]), Some(expressible_symbol) => { self.expressible_symbol = Some(expressible_symbol); - vec![] + Ok(()) } } } - pub fn type_info(&self) -> TypeInfo { - match self.expressible_symbol.as_ref().unwrap() { - ExpressibleSymbol::Function(function_symbol) => { - TypeInfo::Function(function_symbol.clone()) - } - ExpressibleSymbol::Parameter(parameter_symbol) => { - parameter_symbol.borrow().type_info().clone() - } - ExpressibleSymbol::Variable(variable_symbol) => { - variable_symbol.borrow().type_info().clone() - } - } + pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec> { + let type_info = self.expressible_symbol.as_ref().unwrap().type_info(); + self.type_info = Some(type_info); + Ok(()) + } + + pub fn type_info(&self) -> &TypeInfo { + self.type_info.as_ref().unwrap() } pub fn expressible_symbol(&self) -> &ExpressibleSymbol { diff --git a/dmc-lib/src/ast/integer_literal.rs b/dmc-lib/src/ast/integer_literal.rs index 5dd1559..9729ad4 100644 --- a/dmc-lib/src/ast/integer_literal.rs +++ b/dmc-lib/src/ast/integer_literal.rs @@ -1,15 +1,19 @@ use crate::source_range::SourceRange; +use crate::type_info::TypeInfo; pub struct IntegerLiteral { value: i32, source_range: SourceRange, + type_info: &'static TypeInfo, } impl IntegerLiteral { pub fn new(value: i32, source_range: SourceRange) -> Self { + const TYPE_INFO: TypeInfo = TypeInfo::Integer; Self { value, source_range, + type_info: &TYPE_INFO, } } @@ -17,6 +21,10 @@ impl IntegerLiteral { self.value } + pub fn type_info(&self) -> &TypeInfo { + &self.type_info + } + pub fn source_range(&self) -> &SourceRange { &self.source_range } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 9e79af9..1bd9731 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -45,9 +45,19 @@ impl LetStatement { &mut self.initializer } - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { let mut diagnostics = vec![]; - self.initializer_mut().gather_declared_names(symbol_table); + + match self.initializer_mut().gather_declared_names(symbol_table) { + Ok(_) => {} + Err(mut initializer_diagnostics) => { + diagnostics.append(&mut initializer_diagnostics); + } + } + let insert_result = symbol_table.insert_variable_symbol(VariableSymbol::new(self.declared_name())); if let Err(symbol_insert_error) = insert_result { @@ -64,32 +74,35 @@ impl LetStatement { } } } + self.scope_id = Some(symbol_table.current_scope_id()); - diagnostics + + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { self.initializer.check_name_usages(symbol_table) } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { - let initializer_diagnostics = self.initializer.type_check(symbol_table); - if !initializer_diagnostics.is_empty() { - return initializer_diagnostics; - } + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.initializer.type_check(symbol_table)?; let initializer_type_info = self.initializer.type_info(); let variable_symbol = symbol_table.get_variable_symbol(self.scope_id.unwrap(), &self.declared_name); variable_symbol .borrow_mut() - .set_type_info(initializer_type_info); - vec![] + .set_type_info(initializer_type_info.clone()); + Ok(()) } pub fn to_ir(&self, builder: &mut IrBuilder, symbol_table: &SymbolTable) { let init_operation = match self.initializer() { Expression::Call(call) => IrOperation::Call(call.to_ir(builder, symbol_table)), - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { IrOperation::Load(IrExpression::Int(integer_literal.value())) } Expression::String(string_literal) => { @@ -98,7 +111,7 @@ impl LetStatement { Expression::Identifier(identifier) => { IrOperation::Load(identifier.expressible_symbol().ir_expression()) } - Expression::Additive(additive_expression) => { + Expression::Add(additive_expression) => { IrOperation::Add(additive_expression.to_ir(builder, symbol_table)) } Expression::Subtract(subtract_expression) => todo!(), diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index 0f0d6c3..fb681e5 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -1,4 +1,4 @@ -pub mod additive_expression; +pub mod add_expression; pub mod call; pub mod compilation_unit; pub mod expression; diff --git a/dmc-lib/src/ast/module_level_declaration.rs b/dmc-lib/src/ast/module_level_declaration.rs index 4b3c69e..9b778c0 100644 --- a/dmc-lib/src/ast/module_level_declaration.rs +++ b/dmc-lib/src/ast/module_level_declaration.rs @@ -9,7 +9,10 @@ pub enum ModuleLevelDeclaration { } impl ModuleLevelDeclaration { - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { match self { ModuleLevelDeclaration::Function(function) => { function.gather_declared_names(symbol_table) @@ -20,7 +23,7 @@ impl ModuleLevelDeclaration { } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { ModuleLevelDeclaration::Function(function) => function.check_name_usages(symbol_table), ModuleLevelDeclaration::ExternFunction(extern_function) => { @@ -29,7 +32,7 @@ impl ModuleLevelDeclaration { } } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { ModuleLevelDeclaration::Function(function) => function.type_check(symbol_table), ModuleLevelDeclaration::ExternFunction(extern_function) => { diff --git a/dmc-lib/src/ast/negative_expression.rs b/dmc-lib/src/ast/negative_expression.rs index bc96396..762de49 100644 --- a/dmc-lib/src/ast/negative_expression.rs +++ b/dmc-lib/src/ast/negative_expression.rs @@ -1,9 +1,13 @@ use crate::ast::expression::Expression; +use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; +use crate::symbol_table::SymbolTable; +use crate::type_info::TypeInfo; pub struct NegativeExpression { operand: Box, source_range: SourceRange, + type_info: Option, } impl NegativeExpression { @@ -11,6 +15,7 @@ impl NegativeExpression { Self { operand: operand.into(), source_range, + type_info: None, } } @@ -25,4 +30,35 @@ impl NegativeExpression { pub fn operand_mut(&mut self) -> &mut Expression { &mut self.operand } + + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + self.operand.gather_declared_names(symbol_table) + } + + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.operand.check_name_usages(symbol_table) + } + + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.operand.type_check(symbol_table)?; + + let type_info = self.operand.type_info(); + if type_info.can_negate() { + self.type_info = Some(type_info.negate_result()); + Ok(()) + } else { + Err(vec![Diagnostic::new( + &format!("Cannot negate {}", type_info), + self.source_range.start(), + self.source_range.end(), + )]) + } + } + + pub fn type_info(&self) -> &TypeInfo { + self.type_info.as_ref().unwrap() + } } diff --git a/dmc-lib/src/ast/statement.rs b/dmc-lib/src/ast/statement.rs index bb965f7..767a8f5 100644 --- a/dmc-lib/src/ast/statement.rs +++ b/dmc-lib/src/ast/statement.rs @@ -10,7 +10,10 @@ pub enum Statement { } impl Statement { - pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { match self { Statement::Let(let_statement) => let_statement.gather_declared_names(symbol_table), Statement::Expression(expression_statement) => { @@ -19,7 +22,7 @@ impl Statement { } } - pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { Statement::Let(let_statement) => let_statement.check_name_usages(symbol_table), Statement::Expression(expression_statement) => { @@ -28,7 +31,7 @@ impl Statement { } } - pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { match self { Statement::Let(let_statement) => let_statement.type_check(symbol_table), Statement::Expression(expression_statement) => { diff --git a/dmc-lib/src/ast/string_literal.rs b/dmc-lib/src/ast/string_literal.rs index c9c920f..2956ac2 100644 --- a/dmc-lib/src/ast/string_literal.rs +++ b/dmc-lib/src/ast/string_literal.rs @@ -1,15 +1,19 @@ use crate::source_range::SourceRange; +use crate::type_info::TypeInfo; pub struct StringLiteral { content: String, source_range: SourceRange, + type_info: &'static TypeInfo, } impl StringLiteral { pub fn new(content: &str, source_range: SourceRange) -> Self { + const TYPE_INFO: TypeInfo = TypeInfo::Integer; Self { content: content.into(), source_range, + type_info: &TYPE_INFO, } } @@ -17,6 +21,10 @@ impl StringLiteral { &self.content } + pub fn type_info(&self) -> &TypeInfo { + &self.type_info + } + pub fn source_range(&self) -> &SourceRange { &self.source_range } diff --git a/dmc-lib/src/ast/subtract_expression.rs b/dmc-lib/src/ast/subtract_expression.rs index 06a06c3..bfd6e16 100644 --- a/dmc-lib/src/ast/subtract_expression.rs +++ b/dmc-lib/src/ast/subtract_expression.rs @@ -1,10 +1,14 @@ use crate::ast::expression::Expression; +use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; +use crate::symbol_table::SymbolTable; +use crate::type_info::TypeInfo; pub struct SubtractExpression { lhs: Box, rhs: Box, source_range: SourceRange, + type_info: Option, } impl SubtractExpression { @@ -13,6 +17,7 @@ impl SubtractExpression { lhs: lhs.into(), rhs: rhs.into(), source_range, + type_info: None, } } @@ -24,6 +29,62 @@ impl SubtractExpression { &self.rhs } + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + let diagnostics = [&mut self.lhs, &mut self.rhs] + .iter_mut() + .map(|expression| expression.gather_declared_names(symbol_table)) + .filter_map(|result| result.err()) + .flatten() + .collect::>(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } + } + + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let diagnostics: Vec = [&mut self.lhs, &mut self.rhs] + .iter_mut() + .map(|expression| expression.check_name_usages(symbol_table)) + .filter_map(Result::err) + .flatten() + .collect(); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } + } + + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.lhs.type_check(symbol_table)?; + self.rhs.type_check(symbol_table)?; + + let lhs_type_info = self.lhs.type_info(); + let rhs_type_info = self.rhs.type_info(); + if lhs_type_info.can_subtract(rhs_type_info) { + self.type_info = Some(lhs_type_info.add_result(rhs_type_info)); + Ok(()) + } else { + Err(vec![Diagnostic::new( + &format!( + "Incompatible types: cannot subtract {} from {}", + rhs_type_info, lhs_type_info + ), // n.b. order + self.lhs.source_range().start(), + self.lhs.source_range().end(), + )]) + } + } + + pub fn type_info(&self) -> &TypeInfo { + self.type_info.as_ref().unwrap() + } + pub fn source_range(&self) -> &SourceRange { &self.source_range } diff --git a/dmc-lib/src/ir/ir_function.rs b/dmc-lib/src/ir/ir_function.rs index 0b98c80..ffc6b12 100644 --- a/dmc-lib/src/ir/ir_function.rs +++ b/dmc-lib/src/ir/ir_function.rs @@ -23,13 +23,13 @@ impl IrFunction { pub fn new( function_symbol: Rc>, parameters: &[Rc], - return_type_info: TypeInfo, + return_type_info: &TypeInfo, entry: Rc>, ) -> Self { Self { function_symbol, parameters: parameters.to_vec(), - return_type_info, + return_type_info: return_type_info.clone(), entry, } } diff --git a/dmc-lib/src/ir/ir_variable.rs b/dmc-lib/src/ir/ir_variable.rs index 35bdd90..40dce53 100644 --- a/dmc-lib/src/ir/ir_variable.rs +++ b/dmc-lib/src/ir/ir_variable.rs @@ -9,19 +9,19 @@ pub struct IrVariable { } impl IrVariable { - pub fn new_vr(name: Rc, block_id: usize, type_info: TypeInfo) -> Self { + pub fn new_vr(name: Rc, block_id: usize, type_info: &TypeInfo) -> Self { Self { descriptor: IrVariableDescriptor::VirtualRegister(IrVrVariableDescriptor::new( name, block_id, )), - type_info, + type_info: type_info.clone(), } } pub fn new_stack(name: Rc, block_id: usize, type_info: TypeInfo) -> Self { Self { descriptor: IrVariableDescriptor::Stack(IrStackVariableDescriptor::new(name, block_id)), - type_info, + type_info: type_info.clone(), } } diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index cb6cedd..78af7ae 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -1,4 +1,4 @@ -use crate::ast::additive_expression::AdditiveExpression; +use crate::ast::add_expression::AddExpression; use crate::ast::call::Call; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::expression::Expression; @@ -408,8 +408,7 @@ impl<'a> Parser<'a> { let rhs = self.prefix_expression()?; let source_range = SourceRange::new(result.source_range().start(), rhs.source_range().end()); - result = - Expression::Additive(AdditiveExpression::new(result, rhs, source_range)); + result = Expression::Add(AddExpression::new(result, rhs, source_range)); } TokenKind::Minus => { self.advance(); // minus @@ -477,7 +476,7 @@ impl<'a> Parser<'a> { let raw = self.token_text(¤t); let source_range = SourceRange::new(current.start(), current.end()); self.advance(); - Ok(Expression::IntegerLiteral(IntegerLiteral::new( + Ok(Expression::Integer(IntegerLiteral::new( i32::from_str(raw).unwrap(), source_range, ))) @@ -716,7 +715,7 @@ mod concrete_tests { let expression = assert_expression("-1"); match expression { Expression::Negative(negative_expression) => match negative_expression.operand() { - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { assert_eq!(integer_literal.value(), 1); } _ => panic!("Expected integer literal"), @@ -729,9 +728,9 @@ mod concrete_tests { fn add_negative() { let expression = assert_expression("1 + -1"); match expression { - Expression::Additive(add_expression) => { + Expression::Add(add_expression) => { match add_expression.lhs() { - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { assert_eq!(integer_literal.value(), 1); } _ => panic!("Expected integer literal"), @@ -739,7 +738,7 @@ mod concrete_tests { match add_expression.rhs() { Expression::Negative(negative_expression) => { match negative_expression.operand() { - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { assert_eq!(integer_literal.value(), 1); } _ => panic!("Expected integer literal"), @@ -758,13 +757,13 @@ mod concrete_tests { match expression { Expression::Subtract(subtract_expression) => { match subtract_expression.lhs() { - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { assert_eq!(integer_literal.value(), 1); } _ => panic!("Expected integer literal"), } match subtract_expression.rhs() { - Expression::IntegerLiteral(integer_literal) => { + Expression::Integer(integer_literal) => { assert_eq!(integer_literal.value(), 1); } _ => panic!("Expected integer literal"), diff --git a/dmc-lib/src/symbol.rs b/dmc-lib/src/symbol.rs index e8c53ff..fbf3c30 100644 --- a/dmc-lib/src/symbol.rs +++ b/dmc-lib/src/symbol.rs @@ -38,8 +38,8 @@ impl FunctionSymbol { self.parameters.as_ref().unwrap() } - pub fn return_type(&self) -> TypeInfo { - self.return_type.clone() + pub fn return_type_info(&self) -> &TypeInfo { + &self.return_type } pub fn is_platform(&self) -> bool { @@ -134,6 +134,20 @@ pub enum ExpressibleSymbol { } impl ExpressibleSymbol { + pub fn type_info(&self) -> TypeInfo { + match self { + ExpressibleSymbol::Function(function_symbol) => { + TypeInfo::Function(function_symbol.clone()) // n.b. not the return type! + } + ExpressibleSymbol::Parameter(parameter_symbol) => { + parameter_symbol.borrow().type_info().clone() + } + ExpressibleSymbol::Variable(variable_symbol) => { + variable_symbol.borrow().type_info().clone() + } + } + } + pub fn ir_expression(&self) -> IrExpression { match self { ExpressibleSymbol::Function(_) => { diff --git a/dmc-lib/src/type_info.rs b/dmc-lib/src/type_info.rs index db54769..a48081c 100644 --- a/dmc-lib/src/type_info.rs +++ b/dmc-lib/src/type_info.rs @@ -68,11 +68,36 @@ impl TypeInfo { } } - pub fn additive_result(&self, _rhs: &Self) -> TypeInfo { + pub fn add_result(&self, _rhs: &Self) -> TypeInfo { match self { TypeInfo::Integer => TypeInfo::Integer, TypeInfo::String => TypeInfo::String, _ => panic!("Adding things other than integers and strings not yet supported"), } } + + pub fn can_subtract(&self, rhs: &Self) -> bool { + matches!(self, TypeInfo::Integer) && matches!(rhs, TypeInfo::Integer) + } + + pub fn subtract_result(&self, rhs: &Self) -> TypeInfo { + match self { + TypeInfo::Integer => match rhs { + TypeInfo::Integer => TypeInfo::Integer, + _ => panic!(), + }, + _ => panic!(), + } + } + + pub fn can_negate(&self) -> bool { + matches!(self, TypeInfo::Integer) + } + + pub fn negate_result(&self) -> TypeInfo { + match self { + TypeInfo::Integer => TypeInfo::Integer, + _ => panic!(), + } + } } diff --git a/e2e-tests/src/lib.rs b/e2e-tests/src/lib.rs index de304db..69fc199 100644 --- a/e2e-tests/src/lib.rs +++ b/e2e-tests/src/lib.rs @@ -35,19 +35,25 @@ mod e2e_tests { let mut symbol_table = SymbolTable::new(); - let gather_names_diagnostics = compilation_unit.gather_declared_names(&mut symbol_table); - if !gather_names_diagnostics.is_empty() { - report_diagnostics(&gather_names_diagnostics); + match compilation_unit.gather_declared_names(&mut symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_diagnostics(&diagnostics); + } } - let name_usages_diagnostics = compilation_unit.check_name_usages(&symbol_table); - if !name_usages_diagnostics.is_empty() { - report_diagnostics(&name_usages_diagnostics); + match compilation_unit.check_name_usages(&symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_diagnostics(&diagnostics); + } } - let type_check_diagnostics = compilation_unit.type_check(&symbol_table); - if !type_check_diagnostics.is_empty() { - report_diagnostics(&type_check_diagnostics); + match compilation_unit.type_check(&symbol_table) { + Ok(_) => {} + Err(diagnostics) => { + report_diagnostics(&diagnostics); + } } let mut ir_functions = compilation_unit.to_ir(&symbol_table);