diff --git a/src/error.rs b/src/error.rs index 152ab74..1380621 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,8 +1,49 @@ use std::fmt; use std::fmt::Formatter; +use crate::ast::tree::Type; + +/// This is just a wrapper for the actual error types. +/// I have no idea whether this is good design (probably not), +/// but idc for now. Shouldn't be too hard to change the API +/// later on bc each component of the compiler has its own +/// wrappers for instantiating errors anyway. +#[derive(Debug)] +pub struct Error { + e: Box, +} + +impl Error { + pub fn syntax_error(file: String, line: usize, col: usize, msg: String) -> Error { + Error { + e: Box::new(SyntaxError::new(file, line, col, msg)) + } + } + + pub fn type_error(file: String, line: usize, col: usize, expected: Type, actual: Type) -> Error { + Error { + e: Box::new(TypeError::new(file, line, col, expected, actual)) + } + } + + pub fn file(&self) -> &String { + self.e.file() + } + + pub fn line(&self) -> usize { + self.e.line() + } + + pub fn col(&self) -> usize { + self.e.col() + } + + pub fn msg(&self) -> &String { + self.e.msg() + } +} /// Anything that can go wrong while compiling and executing the build script. -pub trait Error { +trait ErrorDetails { /// Name of the file that the error originated from. fn file(&self) -> &String; /// Line (starting from 1) of the first erroneous character. @@ -28,7 +69,14 @@ pub struct SyntaxError { msg: String, } -impl fmt::Display for dyn Error { +#[derive(Debug)] +pub struct TypeError { + pos: Position, + expected: Type, + actual: Type, +} + +impl fmt::Display for dyn ErrorDetails { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{} in {}:{}:{}: {}", self.name(), self.file(), self.line(), self.col(), self.msg()) } @@ -43,7 +91,17 @@ impl SyntaxError { } } -impl Error for SyntaxError { +impl TypeError { + pub fn new(file: String, line: usize, col: usize, expected: Type, actual: Type) -> TypeError { + TypeError { + pos: Position { file, line, col }, + expected, + actual, + } + } +} + +impl ErrorDetails for SyntaxError { fn file(&self) -> &String { &self.pos.file } @@ -64,3 +122,25 @@ impl Error for SyntaxError { "SyntaxError" } } + +impl ErrorDetails for TypeError { + fn file(&self) -> &String { + &self.pos.file + } + + fn line(&self) -> usize { + self.pos.line + } + + fn col(&self) -> usize { + self.pos.col + } + + fn msg(&self) -> &String { + &format!("Expected type {}, got {} instead", self.expected, self.actual) + } + + fn name(&self) -> &str { + "TypeError" + } +} diff --git a/src/lex/mod.rs b/src/lex/mod.rs index 709d3b2..1a96540 100644 --- a/src/lex/mod.rs +++ b/src/lex/mod.rs @@ -6,7 +6,7 @@ use cursor::Cursor; pub(crate) mod token; use token::Token; -use crate::error::SyntaxError; +use crate::error::Error; pub struct Lexer<'a> { file: String, @@ -40,9 +40,9 @@ static KEYWORDS: [KeywordMap; 6] = [ ]; impl Iterator for Lexer<'_> { - type Item = Result; + type Item = Result; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { if self.offset > 0 { let tmp = self.history[self.history.len() - self.offset].clone(); self.offset -= 1; @@ -110,7 +110,7 @@ impl<'a> Lexer<'a> { } } - pub fn peek(&mut self) -> Option> { + pub fn peek(&mut self) -> Option> { let t = self.next()?; self.prev(); Some(t) @@ -122,7 +122,7 @@ impl<'a> Lexer<'a> { Some(prev) } - pub fn expect_kind(&mut self, kind: token::Kind) -> Result { + pub fn expect_kind(&mut self, kind: token::Kind) -> Result { match self.next() { Some(Ok(t)) => if t.kind == kind { Ok(t) @@ -134,14 +134,14 @@ impl<'a> Lexer<'a> { } } - pub fn require_next(&mut self) -> Result { + pub fn require_next(&mut self) -> Result { match self.next() { Some(t) => t, None => self.syntax_error(String::from("Unexpected EOF")), } } - fn read_keyword_or_ident(&mut self) -> Result { + fn read_keyword_or_ident(&mut self) -> Result { let current = self.cursor.current().unwrap(); for kw in &KEYWORDS { // keywords are always at least 2 characters long as per the language spec @@ -154,7 +154,7 @@ impl<'a> Lexer<'a> { self.read_ident() } - fn read_ident(&mut self) -> Result { + fn read_ident(&mut self) -> Result { for c in &mut self.cursor { if !c.is_ascii_alphanumeric() && c != '_' { self.cursor.prev(); @@ -165,13 +165,13 @@ impl<'a> Lexer<'a> { self.token_ok(token::Kind::Ident) } - fn read_comment(&mut self) -> Result { + fn read_comment(&mut self) -> Result { assert_eq!(self.cursor.current(), Some('#')); self.cursor.seek_while(|c| c != '\n'); self.token_ok(token::Kind::Comment) } - fn read_string_literal(&mut self) -> Result { + fn read_string_literal(&mut self) -> Result { assert_eq!(self.cursor.current(), Some('"')); self.cursor.chop(); let mut raw = String::new(); @@ -188,7 +188,7 @@ impl<'a> Lexer<'a> { self.token_ok(token::Kind::StringLiteral) } - fn read_prefix_int_literal(&mut self) -> Result { + fn read_prefix_int_literal(&mut self) -> Result { assert_eq!(self.cursor.next(), Some('0')); match self.cursor.next() { Some('x') => self.read_int_literal(16), @@ -199,7 +199,7 @@ impl<'a> Lexer<'a> { } } - fn read_int_literal(&mut self, base: usize) -> Result { + fn read_int_literal(&mut self, base: usize) -> Result { assert!(base >= 2 && base <= 16); for c in &mut self.cursor { @@ -246,8 +246,8 @@ impl<'a> Lexer<'a> { true } - fn syntax_error(&mut self, msg: String) -> Result { - Err(SyntaxError::new( + fn syntax_error(&mut self, msg: String) -> Result { + Err(Error::syntax_error( self.file.clone(), self.cursor.line(), self.cursor.col(),