From 53df4ec33f8ec601aa4f2560f5a7f96c9079dcdd Mon Sep 17 00:00:00 2001 From: Robert-M-Lucas Date: Tue, 4 Jun 2024 04:51:52 +0100 Subject: [PATCH] Improved error handling --- .idea/workspace.xml | 27 +++++++-- main.why | 4 ++ src/root.rs | 2 +- src/root/compiler/compile.rs | 7 ++- src/root/compiler/compile_function.rs | 19 ++++--- src/root/errors/mod.rs | 2 +- src/root/errors/name_resolver_errors.rs | 8 ++- src/root/name_resolver/name_resolvers.rs | 57 +++++++++++-------- .../resolve_function_signatures.rs | 17 +++--- src/root/name_resolver/resolve_names.rs | 39 +++++++------ src/root/parser/parse.rs | 36 +++++++++--- src/root/parser/parse_function.rs | 5 +- src/root/parser/parse_parameters.rs | 14 +++-- 13 files changed, 151 insertions(+), 86 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 5344698..7c963ad 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -7,14 +7,20 @@ - - - + + + + + + + + + @@ -204,6 +218,7 @@ - \ No newline at end of file diff --git a/main.why b/main.why index 43ec5e5..761d81c 100644 --- a/main.why +++ b/main.why @@ -1,3 +1,7 @@ +struct A { + a: int, + b: int +} fn main() -> int { return 12; diff --git a/src/root.rs b/src/root.rs index 2d02dcb..5e6451d 100644 --- a/src/root.rs +++ b/src/root.rs @@ -80,7 +80,7 @@ pub fn main_args(args: Args) -> Result<(), WError> { print!("Compiling... "); time!( - let assembly = compile(global_table, unprocessed_functions); + let assembly = compile(global_table, unprocessed_functions)?; ); print!("Writing Assembly... "); diff --git a/src/root/compiler/compile.rs b/src/root/compiler/compile.rs index ccc8acc..33dc182 100644 --- a/src/root/compiler/compile.rs +++ b/src/root/compiler/compile.rs @@ -1,10 +1,11 @@ use std::collections::{HashMap, HashSet}; use crate::root::compiler::compile_function::compile_function; +use crate::root::errors::WError; use crate::root::name_resolver::name_resolvers::GlobalDefinitionTable; use crate::root::parser::parse_function::FunctionToken; use crate::root::shared::common::FunctionID; -pub fn compile(global_table: GlobalDefinitionTable, unprocessed_functions: HashMap) -> String { +pub fn compile(global_table: GlobalDefinitionTable, unprocessed_functions: HashMap) -> Result { let mut unprocessed_functions = unprocessed_functions; let mut compiled_functions = HashMap::new(); let mut compiled_len = 0usize; @@ -17,7 +18,7 @@ pub fn compile(global_table: GlobalDefinitionTable, unprocessed_functions: HashM let current_function_token = unprocessed_functions.remove(¤t_function).unwrap(); - let (compiled, called_functions) = compile_function(current_function, current_function_token, &global_table); + let (compiled, called_functions) = compile_function(current_function, current_function_token, &global_table)?; compiled_len += compiled.len() + 10; compiled_functions.insert(current_function, compiled); @@ -41,5 +42,5 @@ pub fn compile(global_table: GlobalDefinitionTable, unprocessed_functions: HashM s += &f; } - s + Ok(s) } \ No newline at end of file diff --git a/src/root/compiler/compile_function.rs b/src/root/compiler/compile_function.rs index 6ae7cfb..27cd5d5 100644 --- a/src/root/compiler/compile_function.rs +++ b/src/root/compiler/compile_function.rs @@ -2,21 +2,22 @@ use std::collections::HashSet; use crate::root::compiler::assembly::utils::{align_16_bytes, align_16_bytes_plus_8, get_function_tag, get_qword_stack_pointer}; use crate::root::compiler::compile_evaluable::compile_evaluable; use crate::root::compiler::local_variable_table::LocalVariableTable; +use crate::root::errors::WError; use crate::root::name_resolver::name_resolvers::{GlobalDefinitionTable, NameResultId}; use crate::root::parser::parse_function::FunctionToken; use crate::root::parser::parse_function::parse_line::LineTokens; use crate::root::shared::common::{FunctionID, LocalAddress}; use crate::root::shared::common::AddressedTypeRef; -pub fn compile_function(fid: FunctionID, function: FunctionToken, global_table: &GlobalDefinitionTable) -> (String, HashSet) { +pub fn compile_function(fid: FunctionID, function: FunctionToken, global_table: &GlobalDefinitionTable) -> Result<(String, HashSet), WError> { let mut local_variables = Box::new(LocalVariableTable::default()); let (_location, _name, return_type, parameters, lines) = function.dissolve(); let return_type = if fid.is_main() { None } else { return_type }; - let return_type = return_type.and_then( - |t| Some(match global_table.resolve_global_name_to_id(&t).unwrap().unwrap() { + let return_type = if let Some((t, loc)) = return_type { + Some(match global_table.resolve_global_name_to_id(&t, &loc)? { NameResultId::Function(_) => todo!(), NameResultId::Type(type_ref) => { if type_ref.indirection().has_indirection() { @@ -25,12 +26,16 @@ pub fn compile_function(fid: FunctionID, function: FunctionToken, global_table: type_ref } NameResultId::NotFound => todo!() - })); + }) + } + else { + None + }; let mut param_address = LocalAddress(8); - for (param_name, param_type) in parameters { - let type_ref = match global_table.resolve_global_name_to_id(¶m_type).unwrap().unwrap() { + for ((param_name, param_name_loc), (param_type, param_type_loc)) in parameters { + let type_ref = match global_table.resolve_global_name_to_id(¶m_type, ¶m_type_loc)? { NameResultId::Function(_) => todo!(), NameResultId::Type(type_ref) => { if type_ref.indirection().has_indirection() { @@ -76,7 +81,7 @@ pub fn compile_function(fid: FunctionID, function: FunctionToken, global_table: final_contents += "\n\tleave\n\tret" } - (final_contents, function_calls) + Ok((final_contents, function_calls)) } fn recursively_compile_lines(fid: FunctionID, lines: &[LineTokens], return_variable: &Option, local_variables: Box, global_table: &GlobalDefinitionTable, function_calls: &mut HashSet) -> (String, Box) { diff --git a/src/root/errors/mod.rs b/src/root/errors/mod.rs index 595dbce..07a7825 100644 --- a/src/root/errors/mod.rs +++ b/src/root/errors/mod.rs @@ -29,7 +29,7 @@ impl WError { impl Display for WError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let text = if let Some(location) = &self.location { - cformat!("Error:\n {}\nAt:\n{}", self.error, location) + cformat!("Error:\n {}\n{}\n", self.error, location) } else { cformat!("Error:\n {}", self.error) diff --git a/src/root/errors/name_resolver_errors.rs b/src/root/errors/name_resolver_errors.rs index 8bc77d5..93b8ac1 100644 --- a/src/root/errors/name_resolver_errors.rs +++ b/src/root/errors/name_resolver_errors.rs @@ -5,5 +5,11 @@ pub enum NRErrors { #[error("No top-level main function found")] NoMain, #[error("Cannot create 'impl' for an indirect type")] - IndirectImpl + IndirectImpl, + #[error("Cannot find the subname ({0}) of a function ({1})")] + FunctionSubname(String, String), + #[error("Cannot find method ({0}) of type ({1})")] + CannotFindMethod(String, String), + #[error("Two attributes found with the same name ({0})")] + SameAttributeName(String) } diff --git a/src/root/name_resolver/name_resolvers.rs b/src/root/name_resolver/name_resolvers.rs index 577c6bd..c2f6f62 100644 --- a/src/root/name_resolver/name_resolvers.rs +++ b/src/root/name_resolver/name_resolvers.rs @@ -3,7 +3,10 @@ use std::path::PathBuf; use std::rc::Rc; use derive_getters::Getters; use crate::root::compiler::local_variable_table::LocalVariableTable; +use crate::root::errors::name_resolver_errors::NRErrors; +use crate::root::errors::WError; use crate::root::name_resolver::resolve_function_signatures::FunctionSignature; +use crate::root::parser::parse::Location; use crate::root::shared::types::Type; use crate::root::parser::parse_function::FunctionToken; use crate::root::parser::parse_name::UnresolvedNameToken; @@ -196,10 +199,10 @@ impl GlobalDefinitionTable { todo!() } - pub fn resolve_global_name_to_id(&self, name: &UnresolvedNameToken) -> Result, ()> { + pub fn resolve_global_name_to_id(&self, name: &UnresolvedNameToken, location: &Location) -> Result { let path = name.location().path(); - fn search_file_level_tree(tree: &Box, name: &UnresolvedNameToken) -> Result, ()> { + fn search_file_level_tree(tree: &Box, name: &UnresolvedNameToken, location: &Location) -> Result, WError> { let base = name.base(); let Some(base) = tree.table.get(base) else { return Ok(None) }; @@ -207,20 +210,22 @@ impl GlobalDefinitionTable { match base { FileLevelTreeNode::Function(fid) => { - if name_iter.next().is_some() || name.indirection().has_indirection() { + if let Some((_, next)) = name_iter.next() { + return Err(WError::n(NRErrors::FunctionSubname(next.clone(), name.base().clone()), location.clone())); + } + if name.indirection().has_indirection() { // TODO - return Err(()); } Ok(Some(NameResultId::Function(*fid))) } FileLevelTreeNode::Type(tid, imp) => { Ok(Some(if let Some((connector, method_name)) = name_iter.next() { - // TODO - let Some(function) = imp.functions.get(method_name) else { return Err(()) }; + let Some(function) = imp.functions.get(method_name) else { + return Err(WError::n(NRErrors::CannotFindMethod(method_name.clone(), name.base().clone()), location.clone())); + }; - // TODO - if name_iter.next().is_some() { - return Err(()); + if let Some((_, next)) = name_iter.next() { + return Err(WError::n(NRErrors::FunctionSubname(next.clone(), method_name.clone()), location.clone())); } // match connector { @@ -246,8 +251,8 @@ impl GlobalDefinitionTable { // * Search current file then others if let Some(tree) = tree { - if let Some(found) = search_file_level_tree(tree, name)? { - return Ok(Some(found)); + if let Some(found) = search_file_level_tree(tree, name, location)? { + return Ok(found); } } @@ -256,20 +261,20 @@ impl GlobalDefinitionTable { continue; } - if let Some(found) = search_file_level_tree(tree, name)? { - return Ok(Some(found)); + if let Some(found) = search_file_level_tree(tree, name, location)? { + return Ok(found); } } if let Some((id, impl_node)) = self.builtin_type_name_table.get(name.base()) { let mut name_iter = name.names().iter(); if let Some((connector, method_name)) = name_iter.next() { - // TODO - let Some(function) = impl_node.functions.get(method_name) else { return Err(()) }; + let Some(function) = impl_node.functions.get(method_name) else { + return Err(WError::n(NRErrors::CannotFindMethod(method_name.clone(), name.base().clone()), location.clone())); + }; - // TODO - if name_iter.next().is_some() { - return Err(()); + if let Some((_, next)) = name_iter.next() { + return Err(WError::n(NRErrors::FunctionSubname(next.clone(), method_name.clone()), location.clone())); } // match connector { @@ -282,22 +287,24 @@ impl GlobalDefinitionTable { // NameConnectors::Static => {} // } - return Ok(Some(NameResultId::Function(*function))); + return Ok(NameResultId::Function(*function)); } else { - return Ok(Some(NameResultId::Type(TypeRef::new(*id, *name.indirection())))); + return Ok(NameResultId::Type(TypeRef::new(*id, *name.indirection()))); } } if let Some(id) = self.builtin_function_name_table.get(name.base()) { - // TODO - if !name.names().is_empty() || name.indirection().has_indirection() { - return Err(()); + if let Some((_, next)) = name.names().iter().next() { + return Err(WError::n(NRErrors::FunctionSubname(next.clone(), name.base().clone()), location.clone())); + } + if name.indirection().has_indirection() { + // TODO } - return Ok(Some(NameResultId::Function(*id))) + return Ok(NameResultId::Function(*id)) } - Ok(None) + todo!() } } diff --git a/src/root/name_resolver/resolve_function_signatures.rs b/src/root/name_resolver/resolve_function_signatures.rs index d776b55..3c943c0 100644 --- a/src/root/name_resolver/resolve_function_signatures.rs +++ b/src/root/name_resolver/resolve_function_signatures.rs @@ -1,4 +1,5 @@ use derive_getters::Getters; +use crate::root::errors::WError; use crate::root::name_resolver::name_resolvers::{GlobalDefinitionTable, NameResult, NameResultId}; use crate::root::shared::common::TypeRef; use crate::root::parser::parse_function::FunctionToken; @@ -9,12 +10,12 @@ pub struct FunctionSignature { return_type: Option } -pub fn resolve_function_signature(function_token: &FunctionToken, global_table: &GlobalDefinitionTable) -> FunctionSignature { +pub fn resolve_function_signature(function_token: &FunctionToken, global_table: &GlobalDefinitionTable) -> Result { let mut args = Vec::new(); - let return_type = if let Some(type_name) = function_token.return_type() { + let return_type = if let Some((type_name, location)) = function_token.return_type() { // TODO - Some(match global_table.resolve_global_name_to_id(type_name).unwrap().unwrap() { + Some(match global_table.resolve_global_name_to_id(type_name, location)? { NameResultId::Function(_) => todo!(), NameResultId::Type(type_id) => type_id, NameResultId::NotFound => todo!() @@ -23,11 +24,11 @@ pub fn resolve_function_signature(function_token: &FunctionToken, global_table: None }; - for (arg_name, arg_type) in function_token.parameters() { + for (arg_name, (arg_type, arg_type_loc)) in function_token.parameters() { args.push(( - arg_name.clone(), + arg_name.0.clone(), // TODO - match global_table.resolve_global_name_to_id(arg_type).unwrap().unwrap() { + match global_table.resolve_global_name_to_id(arg_type, arg_type_loc)? { NameResultId::Function(_) => todo!(), NameResultId::Type(type_ref) => type_ref, NameResultId::NotFound => todo!() @@ -35,8 +36,8 @@ pub fn resolve_function_signature(function_token: &FunctionToken, global_table: )) } - FunctionSignature { + Ok(FunctionSignature { args, return_type - } + }) } diff --git a/src/root/name_resolver/resolve_names.rs b/src/root/name_resolver/resolve_names.rs index 9baa875..4620f94 100644 --- a/src/root/name_resolver/resolve_names.rs +++ b/src/root/name_resolver/resolve_names.rs @@ -115,42 +115,45 @@ pub fn resolve_names(ast: Vec, global_table: &mut GlobalDefiniti TopLevelTokens::Struct(st) => { let (location, _, attributes, id) = st.dissolve(); let id = id.unwrap(); - // TODO: Process indirection - let attributes = attributes.into_iter() - .map(|(name, type_name)| { - // TODO - let type_ref = match global_table.resolve_global_name_to_id(&type_name).unwrap().unwrap() { - NameResultId::Type(type_ref) => type_ref, - NameResultId::Function(_) => todo!(), - NameResultId::NotFound => todo!(), - }; - - (name, type_ref) + + let mut p_attributes = Vec::new(); + for ((name, name_loc), (type_name, type_name_loc)) in attributes { + let type_ref = match global_table.resolve_global_name_to_id(&type_name, &type_name_loc)? { + NameResultId::Type(type_ref) => type_ref, + NameResultId::Function(_) => todo!(), + NameResultId::NotFound => todo!(), + }; + + for (e_name, _) in &p_attributes { + if e_name == &name { + return Err(WError::n(NRErrors::SameAttributeName(name), name_loc)); + } } - ).collect_vec(); - unsized_final_types.insert(id, UnsizedUserType::new(id, attributes, location)); + p_attributes.push((name, type_ref)) + } + unsized_final_types.insert(id, UnsizedUserType::new(id, p_attributes, location)); } TopLevelTokens::Impl(it) => { // TODO - let type_ref = match global_table.resolve_global_name_to_id(&UnresolvedNameToken::new_unresolved_top(it.name().clone(), it.location().clone())).unwrap().unwrap() { + let type_ref = match global_table.resolve_global_name_to_id(&UnresolvedNameToken::new_unresolved_top(it.name().clone(), it.location().clone()), it.location())? { NameResultId::Function(_) => todo!(), NameResultId::Type(type_ref) => type_ref, NameResultId::NotFound => todo!(), }; if type_ref.indirection().has_indirection() { - return WError::n(NRErrors::IndirectImpl, it.location().clone()); + return Err(WError::n(NRErrors::IndirectImpl, it.location().clone())); } for ft in it.dissolve().2 { let function_id = global_table.add_from_function_token(&ft, Some(*type_ref.type_id())); - global_table.add_function_signature(function_id, resolve_function_signature(&ft, &global_table)); + global_table.add_function_signature(function_id, resolve_function_signature(&ft, &global_table)?); unprocessed_functions.insert(function_id, ft); } } TopLevelTokens::Function(ft) => { let function_id = global_table.add_from_function_token(&ft, None); - global_table.add_function_signature(function_id, resolve_function_signature(&ft, &global_table)); + global_table.add_function_signature(function_id, resolve_function_signature(&ft, &global_table)?); unprocessed_functions.insert(function_id, ft); } }; @@ -169,5 +172,5 @@ pub fn resolve_names(ast: Vec, global_table: &mut GlobalDefiniti } // (final_types, type_names, unprocessed_functions) - unprocessed_functions + Ok(unprocessed_functions) } diff --git a/src/root/parser/parse.rs b/src/root/parser/parse.rs index 5a38797..9e6ac32 100644 --- a/src/root/parser/parse.rs +++ b/src/root/parser/parse.rs @@ -7,6 +7,7 @@ use nom_supreme::error::GenericErrorTree; use std::fs; use std::path::PathBuf; use std::rc::Rc; +use color_print::cformat; use derive_getters::Getters; use crate::root::parser::parse_toplevel::TopLevelTokens; @@ -43,30 +44,49 @@ const CHAR_LIMIT: usize = 61; impl Display for Location { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "{}", cformat!("In File:"))?; + writeln!(f, " {}", self.path.as_path().to_string_lossy())?; + writeln!(f, "{}", cformat!("At:"))?; + fn fail(f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "Failed to fetch file reference (has the file changed?") } let Ok(file) = fs::read_to_string(self.path.as_path()) else { return fail(f); }; + let mut offset = 0usize; + let mut chars = file.chars(); + for _ in 0..self.offset { + let Some(c) = chars.next() else { return fail(f); }; + if c == '\n' { + offset = 0; + } + else { + offset += 1; + } + } + println!("O: {offset}"); + let mut line_iter = file.lines(); + let largest_num_len = format!("{}", self.line + 2).len(); + if self.line > 1 { if self.line > 2 { - writeln!(f, "{} | ...", self.line - 2)?; + writeln!(f, "{:0width$} | ...", self.line - 2, width=largest_num_len)?; } let Some(line) = line_iter.nth(self.line as usize - 2) else { return fail(f); }; let line = if line.chars().count() > CHAR_LIMIT { format!("{} ...", line.chars().take(CHAR_LIMIT - 4).collect::()) } else { line.to_string() }; - writeln!(f, "{} | {}", self.line - 1, line)?; + writeln!(f, "{:0width$} | {}", self.line - 1, line, width=largest_num_len)?; } let Some(line) = line_iter.next() else { return fail(f); }; let (mut start, mut end) = (0usize, line.chars().count() - 1); if end > CHAR_LIMIT { - let start_dist = self.offset - start; - let end_dist = end - self.offset; + let start_dist = offset - start; + let end_dist = end - offset; if start_dist > end_dist { let take_from_start = min(start_dist, CHAR_LIMIT / 2); @@ -82,14 +102,14 @@ impl Display for Location { end += 1; - writeln!(f, "{} | {}", self.line, line.chars().skip(start).take(end - start).collect::())?; - writeln!(f, "E | {}^Here", (0..(self.offset - start)).map(|_| ' ').collect::())?; + writeln!(f, "{:0width$} | {}", self.line, line.chars().skip(start).take(end - start).collect::(), width=largest_num_len)?; + writeln!(f, "{:0width$} | {}^Here", "E", (0..(offset - start)).map(|_| ' ').collect::(), width=largest_num_len)?; if let Some(line) = line_iter.next() { let line = if line.chars().count() > CHAR_LIMIT { format!("{} ...", line.chars().take(CHAR_LIMIT - 4).collect::()) } else { line.to_string() }; - writeln!(f, "{} | {}", self.line + 1, line)?; + writeln!(f, "{:0width$} | {}", self.line + 1, line, width=largest_num_len)?; if line_iter.next().is_some() { - writeln!(f, "{} | ...", self.line + 2)?; + writeln!(f, "{:0width$} | ...", self.line + 2, width=largest_num_len)?; } } diff --git a/src/root/parser/parse_function.rs b/src/root/parser/parse_function.rs index a2dcaac..fe885fe 100644 --- a/src/root/parser/parse_function.rs +++ b/src/root/parser/parse_function.rs @@ -27,7 +27,7 @@ pub mod parse_while; pub struct FunctionToken { location: Location, name: String, - return_type: Option, + return_type: Option<(UnresolvedNameToken, Location)>, parameters: Parameters, lines: Vec, } @@ -59,8 +59,9 @@ pub fn parse_function(s: Span, allow_self: Option) -> Parse let (s, return_type) = if let Ok((s, _)) = tag::<_, _, ErrorTree>("->")(s) { let (s, _) = discard_ignored(s)?; + let location = Location::from_span(&s); let (s, return_type) = parse_full_name(s, containing_class.and_then(|s| Some(s.to_string())))?; - (discard_ignored(s)?.0, Some(return_type)) + (discard_ignored(s)?.0, Some((return_type, location))) } else { (s, None) }; diff --git a/src/root/parser/parse_parameters.rs b/src/root/parser/parse_parameters.rs index 8b8b6fb..ed42d95 100644 --- a/src/root/parser/parse_parameters.rs +++ b/src/root/parser/parse_parameters.rs @@ -1,9 +1,10 @@ -use crate::root::parser::parse::{ParseResult, Span}; +use crate::root::parser::parse::{Location, ParseResult, Span}; use crate::root::parser::parse_name::{parse_full_name, parse_simple_name, UnresolvedNameToken}; use nom::character::complete::{char}; +use nom::Offset; use crate::root::parser::parse_util::discard_ignored; -pub type Parameters = Vec<(String, UnresolvedNameToken)>; +pub type Parameters = Vec<((String, Location), (UnresolvedNameToken, Location))>; pub fn parse_parameters(s: Span, mut allow_self: Option) -> ParseResult<(), Parameters> { let (mut s, _) = discard_ignored(s)?; @@ -14,19 +15,20 @@ pub fn parse_parameters(s: Span, mut allow_self: Option) -> while !s.is_empty() { let (ns, name) = parse_simple_name(s)?; - let (ns, type_name_token) = if allow_self.is_some() && parameters.is_empty() && *name.fragment() == "self" { - (ns, allow_self.take().unwrap()) + let (ns, (type_name_token, t_location)) = if allow_self.is_some() && parameters.is_empty() && *name.fragment() == "self" { + (ns, (allow_self.take().unwrap(), Location::from_span(&name))) } else { let (ns, _) = discard_ignored(ns)?; let (ns, _) = char(':')(ns)?; let (ns, _) = discard_ignored(ns)?; + let t_location = Location::from_span(&ns); let (ns, type_name_token) = parse_full_name(ns, self_name.clone())?; let (ns, _) = discard_ignored(ns)?; - (ns, type_name_token) + (ns, (type_name_token, t_location)) }; - parameters.push((name.to_string(), type_name_token)); + parameters.push(((name.to_string(), Location::from_span(&name)), (type_name_token, t_location))); if ns.is_empty() { break;