Attempt to significantly improve error-raporting

This commit is contained in:
Sofia 2025-07-17 16:47:10 +03:00
parent 64e34ecf13
commit f0e47a5d57
12 changed files with 344 additions and 139 deletions

View File

@ -1,6 +1,6 @@
use std::{env, error::Error, fs, path::PathBuf}; use std::{env, fs, path::PathBuf};
use reid::compile; use reid::compile_simple;
use reid_lib::compile::CompileOutput; use reid_lib::compile::CompileOutput;
fn main() -> Result<(), std::io::Error> { fn main() -> Result<(), std::io::Error> {
@ -15,7 +15,7 @@ fn main() -> Result<(), std::io::Error> {
let before = std::time::SystemTime::now(); let before = std::time::SystemTime::now();
let text = fs::read_to_string(&path)?; let text = fs::read_to_string(&path)?;
match compile(&text, PathBuf::from(&path)) { match compile_simple(&text, PathBuf::from(&path)) {
Ok(CompileOutput { Ok(CompileOutput {
triple, triple,
assembly, assembly,

View File

@ -0,0 +1,175 @@
use std::{collections::HashMap, fmt::Debug};
use crate::{
ast, lexer,
mir::{self, pass, Metadata, SourceModuleId},
token_stream,
};
impl<T: std::error::Error + std::fmt::Display> pass::Error<T> {
fn fmt_simple(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.kind, f)
}
}
#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
#[error("Lexing: {0:?}")]
LexerError(#[from] mir::pass::Error<lexer::Error>),
#[error("Parsing: {0:?}")]
ParserError(#[from] mir::pass::Error<token_stream::Error>),
#[error("Typechecking: {0:?}")]
TypeCheckError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
#[error("Type Inference: {0:?}")]
TypeInferenceError(#[source] mir::pass::Error<mir::typecheck::ErrorKind>),
#[error("Linking: {0:?}")]
LinkerError(#[from] mir::pass::Error<mir::linker::ErrorKind>),
}
impl ErrorKind {
pub fn from_typecheck(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeCheckError(err)
}
pub fn from_typeinference(err: mir::pass::Error<mir::typecheck::ErrorKind>) -> ErrorKind {
ErrorKind::TypeInferenceError(err)
}
}
impl ErrorKind {
fn get_meta(&self) -> Metadata {
match &self {
ErrorKind::LexerError(error) => error.metadata,
ErrorKind::ParserError(error) => error.metadata,
ErrorKind::TypeCheckError(error) => error.metadata,
ErrorKind::TypeInferenceError(error) => error.metadata,
ErrorKind::LinkerError(error) => error.metadata,
}
}
}
impl PartialOrd for ErrorKind {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.get_meta()
.source_module_id
.partial_cmp(&other.get_meta().source_module_id)
}
}
impl Ord for ErrorKind {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get_meta().cmp(&other.get_meta())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ModuleMap {
module_map: HashMap<mir::SourceModuleId, String>,
module_counter: mir::SourceModuleId,
}
impl ModuleMap {
pub fn add_module<T: Into<String>>(&mut self, name: T) -> Option<mir::SourceModuleId> {
let id = self.module_counter.increment();
self.module_map.insert(id, name.into().clone());
Some(id)
}
}
impl TryFrom<&mir::Context> for ModuleMap {
type Error = ();
fn try_from(value: &mir::Context) -> Result<Self, Self::Error> {
let mut map = HashMap::new();
for module in &value.modules {
if let Some(_) = map.insert(module.module_id, module.name.clone()) {
return Err(());
}
}
let module_counter = value.modules.iter().map(|m| m.module_id).max().ok_or(())?;
Ok(ModuleMap {
module_map: map,
module_counter,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReidError {
map: ModuleMap,
errors: Vec<ErrorKind>,
}
impl std::error::Error for ReidError {}
impl std::fmt::Display for ReidError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut sorted_errors = self.errors.clone();
sorted_errors.sort_by(|a, b| a.cmp(&b));
let mut curr_module = None;
for error in sorted_errors {
let meta = error.get_meta();
if curr_module != Some(meta.source_module_id) {
curr_module = Some(meta.source_module_id);
writeln!(
f,
"Errors in module {}:",
self.map.module_map.get(&meta.source_module_id).unwrap()
)?;
}
write!(f, " Error: ")?;
std::fmt::Display::fmt(&error, f)?;
writeln!(f, " At: {}", meta)?;
}
Ok(())
}
}
impl ReidError {
pub fn from_lexer<U>(
result: Result<U, lexer::Error>,
map: ModuleMap,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
let pass_err = pass::Error {
metadata: Metadata {
source_module_id: module,
range: Default::default(),
position: Some(*error.get_position()),
},
kind: error,
};
ReidError {
map,
errors: vec![ErrorKind::LexerError(pass_err)],
}
})
}
pub fn from_parser<U>(
result: Result<U, token_stream::Error>,
map: ModuleMap,
module: SourceModuleId,
) -> Result<U, ReidError> {
result.map_err(|error| {
let pass_err = pass::Error {
metadata: Metadata {
source_module_id: module,
range: Default::default(),
position: error.get_position().copied(),
},
kind: error,
};
ReidError {
map,
errors: vec![ErrorKind::ParserError(pass_err)],
}
})
}
pub fn from_kind<U>(errors: Vec<ErrorKind>, map: ModuleMap) -> ReidError {
ReidError { map, errors }
}
}

View File

@ -2,7 +2,7 @@ use std::{fmt::Debug, str::Chars};
static DECIMAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; static DECIMAL_NUMERICS: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone, PartialOrd, Ord)]
pub enum Token { pub enum Token {
/// Values /// Values
Identifier(String), Identifier(String),
@ -273,10 +273,19 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
Ok(tokens) Ok(tokens)
} }
#[derive(thiserror::Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error { pub enum Error {
#[error("Invalid token '{}' at Ln {}, Col {}", .0, (.1).1, (.1).0)] #[error("Invalid token '{}' at Ln {}, Col {}", .0, (.1).1, (.1).0)]
InvalidToken(char, Position), InvalidToken(char, Position),
#[error("String literal that starts at Ln {}, Col {} is never finished!", (.0).1, (.0).0)] #[error("String literal that starts at Ln {}, Col {} is never finished!", (.0).1, (.0).0)]
MissingQuotation(Position), MissingQuotation(Position),
} }
impl Error {
pub fn get_position(&self) -> &Position {
match self {
Error::InvalidToken(_, pos) => pos,
Error::MissingQuotation(pos) => pos,
}
}
}

View File

@ -41,8 +41,9 @@
//! - Debug Symbols //! - Debug Symbols
//! ``` //! ```
use std::path::PathBuf; use std::{convert::Infallible, path::PathBuf};
use error_raporting::{ErrorKind as ErrorRapKind, ModuleMap, ReidError};
use mir::{ use mir::{
linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs, linker::LinkerPass, typecheck::TypeCheck, typeinference::TypeInference, typerefs::TypeRefs,
SourceModuleId, SourceModuleId,
@ -53,34 +54,22 @@ use crate::{ast::TopLevelStatement, lexer::Token, token_stream::TokenStream};
mod ast; mod ast;
mod codegen; mod codegen;
mod error_raporting;
mod lexer; mod lexer;
pub mod mir; pub mod mir;
mod pad_adapter; mod pad_adapter;
mod token_stream; mod token_stream;
mod util; mod util;
#[derive(thiserror::Error, Debug, Clone)] pub fn compile_module<'map>(
pub enum ReidError {
#[error(transparent)]
LexerError(#[from] lexer::Error),
#[error(transparent)]
ParserError(#[from] token_stream::Error),
#[error("Errors during typecheck: {0:?}")]
TypeCheckErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
#[error("Errors during type inference: {0:?}")]
TypeInferenceErrors(Vec<mir::pass::Error<mir::typecheck::ErrorKind>>),
#[error("Errors during linking: {0:?}")]
LinkerErrors(Vec<mir::pass::Error<mir::linker::ErrorKind>>),
}
pub fn compile_module(
source: &str, source: &str,
name: String, name: String,
module_id: SourceModuleId, map: &'map mut ModuleMap,
path: Option<PathBuf>, path: Option<PathBuf>,
is_main: bool, is_main: bool,
) -> Result<mir::Module, ReidError> { ) -> Result<mir::Module, ReidError> {
let tokens = lexer::tokenize(source)?; let id = map.add_module(name.clone()).unwrap();
let tokens = ReidError::from_lexer(lexer::tokenize(source), map.clone(), id)?;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
dbg!(&tokens); dbg!(&tokens);
@ -90,7 +79,8 @@ pub fn compile_module(
let mut statements = Vec::new(); let mut statements = Vec::new();
while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) { while !matches!(token_stream.peek().unwrap_or(Token::Eof), Token::Eof) {
let statement = token_stream.parse::<TopLevelStatement>()?; let statement =
ReidError::from_parser(token_stream.parse::<TopLevelStatement>(), map.clone(), id)?;
statements.push(statement); statements.push(statement);
} }
@ -101,17 +91,24 @@ pub fn compile_module(
is_main, is_main,
}; };
Ok(ast_module.process(module_id)) Ok(ast_module.process(id))
} }
pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> { pub fn perform_all_passes<'map>(
context: &mut mir::Context,
map: &'map mut ModuleMap,
) -> Result<(), ReidError> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
dbg!(&context); dbg!(&context);
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
println!("{}", &context); println!("{}", &context);
let state = context.pass(&mut LinkerPass); let mut module_map = (&*context).try_into().unwrap();
let state = context.pass(&mut LinkerPass {
module_map: &mut module_map,
});
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
println!("{}", &context); println!("{}", &context);
@ -119,7 +116,10 @@ pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> {
dbg!(&state); dbg!(&state);
if !state.errors.is_empty() { if !state.errors.is_empty() {
return Err(ReidError::LinkerErrors(state.errors)); return Err(ReidError::from_kind::<()>(
state.errors.iter().map(|e| e.clone().into()).collect(),
map.clone(),
));
} }
let refs = TypeRefs::default(); let refs = TypeRefs::default();
@ -134,7 +134,14 @@ pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> {
dbg!(&state); dbg!(&state);
if !state.errors.is_empty() { if !state.errors.is_empty() {
return Err(ReidError::TypeInferenceErrors(state.errors)); return Err(ReidError::from_kind::<()>(
state
.errors
.iter()
.map(|e| ErrorRapKind::TypeInferenceError(e.clone()))
.collect(),
map.clone(),
));
} }
let state = context.pass(&mut TypeCheck { refs: &refs }); let state = context.pass(&mut TypeCheck { refs: &refs });
@ -145,7 +152,14 @@ pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> {
dbg!(&state); dbg!(&state);
if !state.errors.is_empty() { if !state.errors.is_empty() {
return Err(ReidError::TypeCheckErrors(state.errors)); return Err(ReidError::from_kind::<()>(
state
.errors
.iter()
.map(|e| ErrorRapKind::TypeCheckError(e.clone()))
.collect(),
map.clone(),
));
} }
Ok(()) Ok(())
@ -154,21 +168,24 @@ pub fn perform_all_passes(context: &mut mir::Context) -> Result<(), ReidError> {
/// Takes in a bit of source code, parses and compiles it and produces `hello.o` /// Takes in a bit of source code, parses and compiles it and produces `hello.o`
/// and `hello.asm` from it, which can be linked using `ld` to produce an /// and `hello.asm` from it, which can be linked using `ld` to produce an
/// executable file. /// executable file.
pub fn compile(source: &str, path: PathBuf) -> Result<CompileOutput, ReidError> { pub fn compile_and_pass<'map>(
source: &str,
path: PathBuf,
module_map: &'map mut ModuleMap,
) -> Result<CompileOutput, ReidError> {
let path = path.canonicalize().unwrap(); let path = path.canonicalize().unwrap();
let mut mir_context = mir::Context::from( let module = compile_module(
vec![compile_module( source,
source, path.file_name().unwrap().to_str().unwrap().to_owned(),
path.file_name().unwrap().to_str().unwrap().to_owned(), module_map,
SourceModuleId::default(), Some(path.clone()),
Some(path.clone()), true,
true, )?;
)?],
path.parent().unwrap().to_owned(),
);
perform_all_passes(&mut mir_context)?; let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());
perform_all_passes(&mut mir_context, module_map)?;
let mut context = Context::new(); let mut context = Context::new();
let codegen_modules = mir_context.codegen(&mut context); let codegen_modules = mir_context.codegen(&mut context);
@ -179,3 +196,8 @@ pub fn compile(source: &str, path: PathBuf) -> Result<CompileOutput, ReidError>
let compiled = codegen_modules.compile(); let compiled = codegen_modules.compile();
Ok(compiled.output()) Ok(compiled.output())
} }
pub fn compile_simple(source: &str, path: PathBuf) -> Result<CompileOutput, ReidError> {
let mut map = ModuleMap::default();
compile_and_pass(source, path, &mut map)
}

View File

@ -279,7 +279,7 @@ impl TypeKind {
} }
} }
#[derive(Debug, Clone, thiserror::Error)] #[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
pub enum EqualsIssue { pub enum EqualsIssue {
#[error("Function is already defined locally at {:?}", (.0).range)] #[error("Function is already defined locally at {:?}", (.0).range)]
ExistsLocally(Metadata), ExistsLocally(Metadata),

View File

@ -7,7 +7,7 @@ use std::{
rc::Rc, rc::Rc,
}; };
use crate::{compile_module, ReidError}; use crate::{compile_module, error_raporting::ModuleMap};
use super::{ use super::{
pass::{Pass, PassState}, pass::{Pass, PassState},
@ -17,14 +17,14 @@ use super::{
pub static STD_SOURCE: &str = include_str!("../../lib/std.reid"); pub static STD_SOURCE: &str = include_str!("../../lib/std.reid");
#[derive(thiserror::Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind { pub enum ErrorKind {
#[error("Unable to import inner modules, not yet supported: {0}")] #[error("Unable to import inner modules, not yet supported: {0}")]
InnerModulesNotYetSupported(Import), InnerModulesNotYetSupported(Import),
#[error("No such module: {0}")] #[error("No such module: {0}")]
ModuleNotFound(String), ModuleNotFound(String),
#[error("Error while compiling module {0}: {1}")] #[error("Error while compiling module {0}: {1}")]
ModuleCompilationError(String, ReidError), ModuleCompilationError(String, String),
#[error("No such function {0} found in module {1}")] #[error("No such function {0} found in module {1}")]
NoSuchFunctionInModule(String, String), NoSuchFunctionInModule(String, String),
#[error("Importing function {0}::{1} not possible: {2}")] #[error("Importing function {0}::{1} not possible: {2}")]
@ -41,11 +41,11 @@ pub enum ErrorKind {
FunctionIsPrivate(String, String), FunctionIsPrivate(String, String),
} }
pub fn compile_std(module_id: SourceModuleId) -> super::Module { pub fn compile_std(module_map: &mut ModuleMap) -> super::Module {
let module = compile_module( let module = compile_module(
STD_SOURCE, STD_SOURCE,
"standard_library".to_owned(), "standard_library".to_owned(),
module_id, module_map,
None, None,
false, false,
) )
@ -59,11 +59,13 @@ pub fn compile_std(module_id: SourceModuleId) -> super::Module {
/// Struct used to implement a type-checking pass that can be performed on the /// Struct used to implement a type-checking pass that can be performed on the
/// MIR. /// MIR.
pub struct LinkerPass; pub struct LinkerPass<'map> {
pub module_map: &'map mut ModuleMap,
}
type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>; type LinkerPassState<'st, 'sc> = PassState<'st, 'sc, (), ErrorKind>;
impl Pass for LinkerPass { impl<'map> Pass for LinkerPass<'map> {
type Data = (); type Data = ();
type TError = ErrorKind; type TError = ErrorKind;
fn context(&mut self, context: &mut Context, mut state: LinkerPassState) { fn context(&mut self, context: &mut Context, mut state: LinkerPassState) {
@ -92,15 +94,9 @@ impl Pass for LinkerPass {
modules.insert(module.name.clone(), Rc::new(RefCell::new(module))); modules.insert(module.name.clone(), Rc::new(RefCell::new(module)));
} }
let mut module_counter = modules
.values()
.map(|m| m.borrow().module_id)
.max()
.unwrap();
modules.insert( modules.insert(
"std".to_owned(), "std".to_owned(),
Rc::new(RefCell::new(compile_std(module_counter.increment()))), Rc::new(RefCell::new(compile_std(&mut self.module_map))),
); );
let mut modules_to_process: Vec<Rc<RefCell<Module>>> = modules.values().cloned().collect(); let mut modules_to_process: Vec<Rc<RefCell<Module>>> = modules.values().cloned().collect();
@ -136,7 +132,7 @@ impl Pass for LinkerPass {
match compile_module( match compile_module(
&source, &source,
module_name.clone(), module_name.clone(),
module_counter.increment(), &mut self.module_map,
Some(file_path), Some(file_path),
false, false,
) { ) {
@ -159,7 +155,10 @@ impl Pass for LinkerPass {
} }
Err(err) => { Err(err) => {
state.ok::<_, Infallible>( state.ok::<_, Infallible>(
Err(ErrorKind::ModuleCompilationError(module_name.clone(), err)), Err(ErrorKind::ModuleCompilationError(
module_name.clone(),
format!("{}", err),
)),
import.1, import.1,
); );
continue; continue;

View File

@ -4,7 +4,7 @@
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use crate::token_stream::TokenRange; use crate::{lexer::Position, token_stream::TokenRange};
mod display; mod display;
pub mod r#impl; pub mod r#impl;
@ -24,10 +24,11 @@ impl SourceModuleId {
} }
} }
#[derive(Debug, Default, Clone, Copy)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Metadata { pub struct Metadata {
pub range: TokenRange,
pub source_module_id: SourceModuleId, pub source_module_id: SourceModuleId,
pub range: TokenRange,
pub position: Option<Position>,
} }
impl std::ops::Add for Metadata { impl std::ops::Add for Metadata {
@ -38,6 +39,7 @@ impl std::ops::Add for Metadata {
Metadata { Metadata {
range: self.range + rhs.range, range: self.range + rhs.range,
source_module_id: self.source_module_id, source_module_id: self.source_module_id,
position: None,
} }
} }
} }
@ -47,11 +49,12 @@ impl TokenRange {
Metadata { Metadata {
range: self, range: self,
source_module_id: module, source_module_id: module,
position: None,
} }
} }
} }
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, PartialOrd, Ord)]
pub enum TypeKind { pub enum TypeKind {
#[error("bool")] #[error("bool")]
Bool, Bool,
@ -89,7 +92,7 @@ pub enum TypeKind {
Vague(#[from] VagueType), Vague(#[from] VagueType),
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error, PartialOrd, Ord)]
pub enum VagueType { pub enum VagueType {
#[error("Unknown")] #[error("Unknown")]
Unknown, Unknown,
@ -125,7 +128,7 @@ impl TypeKind {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Literal { pub enum Literal {
I8(i8), I8(i8),
I16(i16), I16(i16),
@ -142,7 +145,7 @@ pub enum Literal {
Vague(VagueLiteral), Vague(VagueLiteral),
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum VagueLiteral { pub enum VagueLiteral {
Number(u64), Number(u64),
} }
@ -204,7 +207,7 @@ pub enum ReturnKind {
#[derive(Debug)] #[derive(Debug)]
pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata); pub struct NamedVariableRef(pub TypeKind, pub String, pub Metadata);
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Import(pub Vec<String>, pub Metadata); pub struct Import(pub Vec<String>, pub Metadata);
#[derive(Debug)] #[derive(Debug)]

View File

@ -15,10 +15,10 @@ pub enum SimplePassError {
VariableAlreadyDefined(String), VariableAlreadyDefined(String),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Error<TErr: STDError> { pub struct Error<TErr: STDError> {
metadata: Metadata, pub metadata: Metadata,
kind: TErr, pub kind: TErr,
} }
impl<TErr: STDError> std::fmt::Display for Error<TErr> { impl<TErr: STDError> std::fmt::Display for Error<TErr> {

View File

@ -10,7 +10,7 @@ use super::{
typerefs::TypeRefs, typerefs::TypeRefs,
}; };
#[derive(thiserror::Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorKind { pub enum ErrorKind {
#[error("NULL error, should never occur!")] #[error("NULL error, should never occur!")]
Null, Null,

View File

@ -174,7 +174,7 @@ impl Drop for TokenStream<'_, '_> {
/// Index-range that can be used with the original array of [`FullToken`]s to /// Index-range that can be used with the original array of [`FullToken`]s to
/// retrieve the precise location of a failure. /// retrieve the precise location of a failure.
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TokenRange { pub struct TokenRange {
pub start: usize, pub start: usize,
pub end: usize, pub end: usize,
@ -207,7 +207,7 @@ impl std::iter::Sum for TokenRange {
} }
} }
#[derive(thiserror::Error, Debug, Clone)] #[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Error { pub enum Error {
#[error("Expected {} at Ln {}, Col {}, got {:?}", .0, (.2).1, (.2).0, .1)] #[error("Expected {} at Ln {}, Col {}, got {:?}", .0, (.2).1, (.2).0, .1)]
Expected(String, Token, Position), Expected(String, Token, Position),
@ -220,3 +220,14 @@ pub enum Error {
#[error("Condition failed for parse-if. Should never be returned to end-user.")] #[error("Condition failed for parse-if. Should never be returned to end-user.")]
IfFailed, IfFailed,
} }
impl Error {
pub fn get_position(&self) -> Option<&Position> {
match self {
Error::Expected(_, _, pos) => Some(pos),
Error::FileEmpty => None,
Error::Undefined => None,
Error::IfFailed => None,
}
}
}

View File

@ -7,88 +7,70 @@ use util::assert_err;
mod util; mod util;
fn test(source: &str, name: &str) {
let mut map = Default::default();
let module = assert_err(compile_module(
source,
name.to_owned(),
&mut map,
None,
true,
));
assert_err(perform_all_passes(
&mut mir::Context {
modules: vec![module],
base: Default::default(),
},
&mut map,
));
}
pub static ARRAY: &str = include_str!("../../reid_src/array.reid"); pub static ARRAY: &str = include_str!("../../reid_src/array.reid");
pub static FIBONACCI: &str = include_str!("../../reid_src/fibonacci.reid"); pub static FIBONACCI: &str = include_str!("../../reid_src/fibonacci.reid");
pub static HELLO_WORLD: &str = include_str!("../../reid_src/hello_world.reid"); pub static HELLO_WORLD: &str = include_str!("../../reid_src/hello_world.reid");
pub static MUTABLE: &str = include_str!("../../reid_src/mutable.reid"); pub static MUTABLE: &str = include_str!("../../reid_src/mutable.reid");
pub static STRINGS: &str = include_str!("../../reid_src/strings.reid"); pub static STRINGS: &str = include_str!("../../reid_src/strings.reid");
pub static ARRAYS: &str = include_str!("../../reid_src/array.reid");
pub static STRUCTS: &str = include_str!("../../reid_src/struct.reid");
pub static ARRAY_STRUCTS: &str = include_str!("../../reid_src/array_structs.reid");
#[test] #[test]
fn array_compiles_well() { fn array_compiles_well() {
let module = assert_err(compile_module( test(ARRAY, "array");
ARRAY,
"array".to_owned(),
Default::default(),
None,
true,
));
assert_err(perform_all_passes(&mut mir::Context {
modules: vec![module],
base: Default::default(),
}));
} }
#[test] #[test]
fn fibonacci_compiles_well() { fn fibonacci_compiles_well() {
let module = assert_err(compile_module( test(FIBONACCI, "fibonacci");
FIBONACCI,
"fibonacci".to_owned(),
Default::default(),
None,
true,
));
assert_err(perform_all_passes(&mut mir::Context {
modules: vec![module],
base: Default::default(),
}));
} }
#[test] #[test]
fn hello_world_compiles_well() { fn hello_world_compiles_well() {
let module = assert_err(compile_module( test(HELLO_WORLD, "hello_world");
HELLO_WORLD,
"hello_world".to_owned(),
Default::default(),
None,
true,
));
assert_err(perform_all_passes(&mut mir::Context {
modules: vec![module],
base: Default::default(),
}));
} }
#[test] #[test]
fn mutable_compiles_well() { fn mutable_compiles_well() {
let module = assert_err(compile_module( test(MUTABLE, "mutable");
MUTABLE,
"mutable".to_owned(),
Default::default(),
None,
true,
));
assert_err(perform_all_passes(&mut mir::Context {
modules: vec![module],
base: Default::default(),
}));
} }
#[test] #[test]
fn strings_compiles_well() { fn strings_compiles_well() {
let module = assert_err(compile_module( test(STRINGS, "strings");
STRINGS, }
"strings".to_owned(),
Default::default(), #[test]
None, fn arrays_compiles_well() {
true, test(ARRAY, "array");
)); }
assert_err(perform_all_passes(&mut mir::Context { #[test]
modules: vec![module], fn struct_compiles_well() {
base: Default::default(), test(STRUCTS, "struct");
})); }
#[test]
fn array_structs_compiles_well() {
test(ARRAY_STRUCTS, "array_structs");
} }

View File

@ -8,18 +8,22 @@ mod util;
#[test] #[test]
fn compiles() { fn compiles() {
let _ = compile_std(Default::default()); let _ = compile_std(&mut Default::default());
} }
#[test] #[test]
fn passes_all_passes() { fn passes_all_passes() {
let mut std = compile_std(Default::default()); let mut map = Default::default();
let mut std = compile_std(&mut map);
// Needed to pass linker-pass // Needed to pass linker-pass
std.is_main = true; std.is_main = true;
assert_err(perform_all_passes(&mut mir::Context { assert_err(perform_all_passes(
modules: vec![std], &mut mir::Context {
base: Default::default(), modules: vec![std],
})); base: Default::default(),
},
&mut map,
));
} }