diff --git a/examples/hello.sloth b/examples/hello.sloth index 619a5a0..8abb65c 100644 --- a/examples/hello.sloth +++ b/examples/hello.sloth @@ -1,5 +1,4 @@ fn main() Int { print("Hello World\n"); - var x: F = 2; return 0; } diff --git a/sloth/src/lexer.rs b/sloth/src/lexer.rs index 20379a3..01eeb46 100644 --- a/sloth/src/lexer.rs +++ b/sloth/src/lexer.rs @@ -2,6 +2,7 @@ //! TODO: Lexing Regex Literals +use std::fmt::Display; use std::str::Chars; use thiserror::Error; @@ -112,6 +113,86 @@ pub enum TokenType { Error(LexerError), } +impl Display for TokenType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + TokenType::DocComment => "##", + TokenType::Comment => "#", + TokenType::OpeningParen => "(", + TokenType::ClosingParen => ")", + TokenType::OpeningBracket => "[", + TokenType::ClosingBracket => "]", + TokenType::OpeningBrace => "{", + TokenType::ClosingBrace => "}", + TokenType::Plus => "+", + TokenType::PlusPlus => "++", + TokenType::Minus => "-", + TokenType::Star => "*", + TokenType::StarStar => "**", + TokenType::Slash => "/", + TokenType::Perc => "%", + TokenType::Tilde => "~", + TokenType::PlusEq => "+=", + TokenType::PlusPlusEq => "++=", + TokenType::MinusEq => "-=", + TokenType::StarEq => "*=", + TokenType::StarStarEq => "**=", + TokenType::SlashEq => "/=", + TokenType::PercEq => "%=", + TokenType::TildeEq => "~=", + TokenType::Amp => "&", + TokenType::AmpAmp => "&&", + TokenType::Pipe => "|", + TokenType::PipePipe => "||", + TokenType::Caret => "^", + TokenType::Eq => "=", + TokenType::EqEq => "==", + TokenType::Bang => "!", + TokenType::BangBang => "!!", + TokenType::BangEq => "!=", + TokenType::Lt => "<", + TokenType::LtLt => "<<", + TokenType::LtEq => "<=", + TokenType::LtLtEq => "<<=", + TokenType::Gt => ">", + TokenType::GtGt => ">>", + TokenType::GtEq => ">=", + TokenType::GtGtEq => ">>=", + TokenType::Comma => ",", + TokenType::Question => "?", + TokenType::QuestionDot => "?.", + TokenType::QuestionQuestion => "??", + TokenType::Dot => ".", + TokenType::DotDot => "..", + TokenType::Colon => ":", + TokenType::ColonColon => "::", + TokenType::SemiColon => ";", + TokenType::Arrow => "->", + TokenType::FatArrow => "=>", + TokenType::Const => "const", + TokenType::Val => "val", + TokenType::Var => "var", + TokenType::Fn => "fn", + TokenType::Return => "return", + TokenType::If => "if", + TokenType::Else => "else", + TokenType::While => "while", + TokenType::For => "for", + TokenType::In => "in", + TokenType::Loop => "loop", + TokenType::Break => "break", + TokenType::Continue => "continue", + TokenType::As => "as", + TokenType::Foreign => "foreign", + TokenType::Literal(_) => "literal", + TokenType::Identifier(_) => "identifier", + TokenType::Error(_) => "error", + }; + + write!(f, "{s}") + } +} + #[derive(Debug, Clone, PartialEq)] pub enum Literal { Integer(i32), diff --git a/sloth/src/main.rs b/sloth/src/main.rs index 421bbea..a1a4756 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -54,7 +54,18 @@ fn main() { // Parsing let tokens = Lexer::new(&source).collect_vec(); let global_symtable = mk_symtable(); - let mut ast = AstParser::parse(tokens, global_symtable).unwrap(); + + let mut ast = match AstParser::parse(tokens, global_symtable) { + Ok(node) => node, + Err(error) => { + eprintln!( + "Error in file {} on line {}: {error}", + args[1 + (error.line() / 1_000) as usize], + error.line() % 1000 + 1, + ); + return; + } + }; if let Err(error) = analyze(&mut ast) { eprintln!( diff --git a/sloth/src/parser/expr.rs b/sloth/src/parser/expr.rs index 3decf5d..ac7a19e 100644 --- a/sloth/src/parser/expr.rs +++ b/sloth/src/parser/expr.rs @@ -87,7 +87,7 @@ impl<'a> AstParser<'a> { ExprKind::Grouping(Box::new(expr)) } - _ => return Err(ParsingError::UnexpectedToken), + tt => return Err(ParsingError::UnexpectedToken(self.line, tt, "")), }; Ok(Expr::new( diff --git a/sloth/src/parser/mod.rs b/sloth/src/parser/mod.rs index a1f251e..09a26fd 100644 --- a/sloth/src/parser/mod.rs +++ b/sloth/src/parser/mod.rs @@ -11,8 +11,17 @@ use crate::symtable::SymbolTable; pub enum ParsingError { #[error("Invalid operation")] InvalidOp, - #[error("Unexpected token")] - UnexpectedToken, + #[error("Unexpected token '{1}'. {2}")] + UnexpectedToken(u32, TokenType, &'static str), +} + +impl ParsingError { + pub fn line(&self) -> u32 { + match &self { + ParsingError::InvalidOp => 0, + ParsingError::UnexpectedToken(x, _, _) => *x, + } + } } #[derive(Debug)] @@ -92,10 +101,13 @@ impl<'a> AstParser<'a> { self.advance_if(|it| it.tt == *next) } - pub fn consume(&mut self, next: TokenType, error: &str) -> Result<&Token, ParsingError> { + pub fn consume( + &mut self, + next: TokenType, + error: &'static str, + ) -> Result<&Token, ParsingError> { if std::mem::discriminant(&self.peek().tt) != std::mem::discriminant(&next) { - println!("{error} at index {:?}", self.index); - return Err(ParsingError::UnexpectedToken); + return Err(ParsingError::UnexpectedToken(self.line, next, error)); } Ok(self.advance().unwrap()) @@ -103,7 +115,7 @@ impl<'a> AstParser<'a> { pub fn consume_literal(&mut self) -> Result { let Some(TokenType::Literal(literal)) = self.advance().map(|it| it.tt.clone()) else { - return Err(ParsingError::UnexpectedToken); + return Err(ParsingError::UnexpectedToken(self.line, self.peek().tt.clone(), "Expected literal")); }; Ok(literal.into()) @@ -111,7 +123,7 @@ impl<'a> AstParser<'a> { pub fn consume_identifier(&mut self) -> Result { let Some(TokenType::Identifier(identifier)) = self.advance().map(|it| it.tt.clone()) else { - return Err(ParsingError::UnexpectedToken); + return Err(ParsingError::UnexpectedToken(self.line, self.peek().tt.clone(), "Expected identifier")); }; Ok(identifier) diff --git a/sloth/src/parser/stmt.rs b/sloth/src/parser/stmt.rs index d8a0509..6af4634 100644 --- a/sloth/src/parser/stmt.rs +++ b/sloth/src/parser/stmt.rs @@ -24,10 +24,10 @@ impl<'a> AstParser<'a> { // Consume the foreign token self.consume(TokenType::Foreign, "Expected foreign")?; - match self.peek().tt { + match &self.peek().tt { TokenType::Fn => self.define_function(true), - _ => Err(ParsingError::UnexpectedToken), + tt => Err(ParsingError::UnexpectedToken(self.line, tt.clone(), "")), } }