diff options
| author | Mel <einebeere@gmail.com> | 2021-11-21 19:44:47 +0100 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2021-11-21 19:44:47 +0100 |
| commit | 4e9d846fbd51cec9e8d931c4aa3f399668ac316e (patch) | |
| tree | cda2e8645662b6c083add5ac87a279381eec054f /src | |
| parent | abaa72af8a1be00f3f84d7771a7994bfbbccf719 (diff) | |
| download | rabbithole-4e9d846fbd51cec9e8d931c4aa3f399668ac316e.tar.zst rabbithole-4e9d846fbd51cec9e8d931c4aa3f399668ac316e.zip | |
Basic errors for parsing.
Diffstat (limited to 'src')
| -rw-r--r-- | src/error.rs | 80 | ||||
| -rw-r--r-- | src/lex/token.rs | 60 | ||||
| -rw-r--r-- | src/main.rs | 18 | ||||
| -rw-r--r-- | src/parse/macros.rs | 22 | ||||
| -rw-r--r-- | src/parse/mod.rs | 2 | ||||
| -rw-r--r-- | src/parse/parser.rs | 107 |
6 files changed, 248 insertions, 41 deletions
diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..7493681 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,80 @@ +use crate::{interpret::walker::WalkerError, lex::token::Location, parse::parser::ParserError}; +use colored::Colorize; +use std::fmt::Display; +pub struct RHError { + pub at: ErrorLocation, + pub kind: RHErrorKind, +} + +impl RHError { + pub fn hydrate_source<'s>(self, source: &'s str) -> RHErrorWithSource<'s> { + RHErrorWithSource { + error: self, + source, + } + } +} + +pub enum RHErrorKind { + Parse(ParserError), + Run(WalkerError), +} + +pub enum ErrorLocation { + Specific(Location), + Eof, +} + +pub struct RHErrorWithSource<'source> { + error: RHError, + source: &'source str, +} + +impl Display for RHErrorWithSource<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (error_title, error_string) = match &self.error.kind { + RHErrorKind::Parse(parser_error) => ( + "Encountered error during parsing", + format!("{}", parser_error), + ), + RHErrorKind::Run(walker_error) => { + ("Encountered runtime error", format!("{}", walker_error)) + } + }; + + let weird_source_error_msg = + "Found error but could not get it's location in source, how does this even happen?"; + + let (line_number, line_column, source_line) = match self.error.at { + ErrorLocation::Specific(location) => ( + location.row, + location.col, + self.source + .lines() + .nth(location.row) + .expect(weird_source_error_msg), + ), + ErrorLocation::Eof => { + let lines = self.source.lines(); + let (line_count, _) = lines.size_hint(); + let last_line = lines.last().expect(weird_source_error_msg); + + (line_count, last_line.len(), last_line) + } + }; + + let arrow_pad_len = line_column + 3 + line_number.to_string().len(); + + writeln!(f, "{} {}:\n", " Error ".on_red(), error_title)?; + writeln!( + f, + "{}{}", + format!("{} | ", line_number).dimmed(), + source_line + )?; + writeln!(f, "{}{}", " ".repeat(arrow_pad_len), "^".red())?; + writeln!(f, "{}", error_string)?; + + Ok(()) + } +} diff --git a/src/lex/token.rs b/src/lex/token.rs index f3dafa7..9d6ded0 100644 --- a/src/lex/token.rs +++ b/src/lex/token.rs @@ -1,3 +1,5 @@ +use std::fmt::{write, Display}; + #[derive(Clone, Copy, Debug)] pub struct Location { pub col: usize, @@ -81,3 +83,61 @@ pub enum TokenKind { Unknown(char), Eof, } + +impl Display for TokenKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TokenKind::OpPlus => write!(f, "+"), + TokenKind::OpMinus => write!(f, "-"), + TokenKind::OpStar => write!(f, "*"), + TokenKind::OpSlash => write!(f, "/"), + TokenKind::OpEq => write!(f, "=="), + TokenKind::OpNot => write!(f, "!"), + TokenKind::OpNeq => write!(f, "!="), + TokenKind::OpLt => write!(f, "<"), + TokenKind::OpGt => write!(f, ">"), + TokenKind::OpLte => write!(f, "<="), + TokenKind::OpGte => write!(f, ">="), + TokenKind::OpAnd => write!(f, "&&"), + TokenKind::OpOr => write!(f, "||"), + TokenKind::Dot => write!(f, "."), + TokenKind::Comma => write!(f, ","), + TokenKind::Colon => write!(f, ":"), + TokenKind::SemiColon => write!(f, ";"), + TokenKind::Assign => write!(f, "="), + TokenKind::ConstAssign => write!(f, ":="), + TokenKind::Arrow => write!(f, "->"), + TokenKind::GroupOpen => write!(f, "("), + TokenKind::GroupClose => write!(f, ")"), + // {{ Escapes a single { + TokenKind::BlockOpen => write!(f, "{{"), + TokenKind::BlockClose => write!(f, "}}"), + TokenKind::ArrayOpen => write!(f, "["), + TokenKind::ArrayClose => write!(f, "]"), + TokenKind::Int(v) => write!(f, "{}", v), + TokenKind::Float(v) => write!(f, "{}", v), + // These are a bit weird, because they aren't direct mappi + TokenKind::Str(v) => write!(f, "\"{}\"", v), + TokenKind::StrOpen => write!(f, "\""), + TokenKind::StrClose => write!(f, "\""), + TokenKind::StrEmbed(v) => write!(f, "{{{}}}", v), + TokenKind::Ident(v) => write!(f, "{}", v), + TokenKind::KeywordFn => write!(f, "fn"), + TokenKind::KeywordIf => write!(f, "if"), + TokenKind::KeywordElif => write!(f, "elif"), + TokenKind::KeywordElse => write!(f, "else"), + TokenKind::KeywordLoop => write!(f, "loop"), + TokenKind::KeywordForm => write!(f, "form"), + TokenKind::KeywordType => write!(f, "type"), + TokenKind::KeywordTrue => write!(f, "true"), + TokenKind::KeywordFalse => write!(f, "false"), + TokenKind::KeywordSelf => write!(f, "self"), + TokenKind::KeywordBreak => write!(f, "break"), + TokenKind::KeywordContinue => write!(f, "continue"), + TokenKind::KeywordReturn => write!(f, "return"), + TokenKind::KeywordPrint => write!(f, "print"), + TokenKind::Unknown(v) => write!(f, "{}", v), + TokenKind::Eof => write!(f, "end of file"), + } + } +} diff --git a/src/main.rs b/src/main.rs index 0df0483..63f2347 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,12 @@ -use std::{env, fs, path::Path}; +use std::{env, fs, path::Path, process::exit}; +mod error; mod interpret; mod lex; mod parse; mod types; +use error::RHError; use lex::lexer::Lexer; use interpret::walker::Walker; @@ -27,7 +29,7 @@ fn file(filename: impl AsRef<Path>) { let lexer = Lexer::new(&contents); let mut parser = Parser::new(lexer); - let node = parser.parse().expect("Failed parsing."); + let node = handle_error(parser.parse(), &contents); let mut walker = Walker::root(); walker.walk(&node); @@ -52,9 +54,19 @@ fn repl() { let lexer = Lexer::new(input_buffer.trim()); let mut parser = Parser::new(lexer); - let node = parser.expression().expect("Failed parsing."); + let node = handle_error(parser.expression(), &input_buffer); let result = walker.walk_expression(&node).expect("Failed interpreting."); println!("🥕: {:?}\n", result); } } + +fn handle_error<T>(result: Result<T, RHError>, source: &str) -> T { + match result { + Ok(x) => x, + Err(error) => { + print!("{}", error.hydrate_source(&source)); + exit(1); + } + } +} diff --git a/src/parse/macros.rs b/src/parse/macros.rs index 97e0104..9cf995c 100644 --- a/src/parse/macros.rs +++ b/src/parse/macros.rs @@ -16,15 +16,15 @@ macro_rules! consume { if let Token {kind: $( $kind )|+, ..} = token { Ok(token) } else { - Err(anyhow!( - // Make a better error message - "Received unexpected token: '{:?}'.", - token.kind - )) + Err(parser_error(ErrorLocation::Specific(token.location), ParserError::UnexpectedToken { + received: token.kind, + expected: merge_token_names!($($kind),+), + })) } } else { - // Here too - Err(anyhow!("Expected a token.")) + Err(parser_error(ErrorLocation::Eof, ParserError::UnexpectedEof { + expected: merge_token_names!($($kind),+), + })) } }; } @@ -49,3 +49,11 @@ macro_rules! inner { } }; } + +// TODO: Better names for the tokens. +#[macro_export] +macro_rules! merge_token_names { + ($($kind:pat_param),+) => { + vec![$( stringify!($kind) ),+].join(", ") + }; +} diff --git a/src/parse/mod.rs b/src/parse/mod.rs index e45e6fa..9ddca42 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,3 +1,3 @@ pub mod ast; mod macros; -pub mod parser; +pub mod parser; \ No newline at end of file diff --git a/src/parse/parser.rs b/src/parse/parser.rs index 8b43a74..76bbab7 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -1,15 +1,17 @@ +use thiserror::Error; + use super::ast::expression::Expression; use super::ast::nodes::{ArrayNode, LoopNode, StrNode, UnaryOperator}; use super::ast::statement::Statement; use super::ast::Program; +use crate::error::{ErrorLocation, RHError, RHErrorKind}; use crate::lex::lexer::Lexer; -use crate::lex::token::TokenKind::*; +use crate::lex::token::TokenKind::{self, *}; use crate::parse::ast::nodes::{ ArrayAccessNode, BinaryOperator, BlockNode, CallNode, ConditionalBlock, FnHeader, FnNode, IfNode, MemberAccessNode, SimpleLiteral, StrPart, TypedIdentifier, }; -use crate::{check, consume, consume_if, inner, lex::token::Token}; -use anyhow::{anyhow, Result}; +use crate::{check, consume, consume_if, inner, lex::token::Token, merge_token_names}; use std::iter::Peekable; pub struct Parser<T: Iterator> { @@ -23,7 +25,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { } } - pub fn parse(&mut self) -> Result<Program> { + pub fn parse(&mut self) -> Result<Program, RHError> { let mut statements = Vec::new(); while !check!(self, Eof) { @@ -33,7 +35,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(Program { statements }) } - fn statement(&mut self) -> Result<Statement> { + fn statement(&mut self) -> Result<Statement, RHError> { let token = self.tokens.peek().expect("Expected token."); match token.kind { KeywordPrint => self.print_statement(), @@ -44,14 +46,14 @@ impl<T: Iterator<Item = Token>> Parser<T> { } } - fn return_statement(&mut self) -> Result<Statement> { + fn return_statement(&mut self) -> Result<Statement, RHError> { consume!(self, KeywordReturn)?; let expression = self.expression()?; consume!(self, SemiColon)?; Ok(Statement::Return(expression)) } - fn break_statement(&mut self) -> Result<Statement> { + fn break_statement(&mut self) -> Result<Statement, RHError> { consume!(self, KeywordBreak)?; let returned_on_break = if consume_if!(self, SemiColon).is_none() { let expression = self.expression()?; @@ -63,30 +65,30 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(Statement::Break(returned_on_break)) } - fn continue_statement(&mut self) -> Result<Statement> { + fn continue_statement(&mut self) -> Result<Statement, RHError> { consume!(self, KeywordContinue)?; consume!(self, SemiColon)?; Ok(Statement::Continue) } - fn print_statement(&mut self) -> Result<Statement> { + fn print_statement(&mut self) -> Result<Statement, RHError> { consume!(self, KeywordPrint)?; let expression = self.expression()?; consume!(self, SemiColon)?; Ok(Statement::Print(expression)) } - fn expression_statement(&mut self) -> Result<Statement> { + fn expression_statement(&mut self) -> Result<Statement, RHError> { let expression = self.expression()?; consume!(self, SemiColon)?; Ok(Statement::Expression(expression)) } - pub fn expression(&mut self) -> Result<Expression> { + pub fn expression(&mut self) -> Result<Expression, RHError> { self.assignment_expression() } - fn assignment_expression(&mut self) -> Result<Expression> { + fn assignment_expression(&mut self) -> Result<Expression, RHError> { // Parse any expressions as l-values for now. let left = self.or_expression()?; @@ -103,7 +105,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { } } - fn or_expression(&mut self) -> Result<Expression> { + fn or_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.and_expression()?; while let Some(op) = consume_if!(self, OpAnd) { @@ -119,7 +121,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn and_expression(&mut self) -> Result<Expression> { + fn and_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.equality_expression()?; while let Some(op) = consume_if!(self, OpOr) { @@ -135,7 +137,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn equality_expression(&mut self) -> Result<Expression> { + fn equality_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.comparison_expression()?; while let Some(op) = consume_if!(self, OpEq | OpNeq) { @@ -151,7 +153,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn comparison_expression(&mut self) -> Result<Expression> { + fn comparison_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.term_expression()?; while let Some(op) = consume_if!(self, OpGt | OpGte | OpLt | OpLte) { @@ -167,7 +169,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn term_expression(&mut self) -> Result<Expression> { + fn term_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.factor_expression()?; while let Some(op) = consume_if!(self, OpPlus | OpMinus) { @@ -183,7 +185,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn factor_expression(&mut self) -> Result<Expression> { + fn factor_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.unary_expression()?; while let Some(op) = consume_if!(self, OpSlash | OpStar) { @@ -199,7 +201,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn unary_expression(&mut self) -> Result<Expression> { + fn unary_expression(&mut self) -> Result<Expression, RHError> { let expression = if check!(self, OpPlus | OpMinus | OpNot) { Expression::Unary { op: UnaryOperator::from_token(self.tokens.next().unwrap()), @@ -212,7 +214,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(expression) } - fn postfix_expression(&mut self) -> Result<Expression> { + fn postfix_expression(&mut self) -> Result<Expression, RHError> { let mut left = self.unit_expression()?; while let Some(token) = consume_if!(self, GroupOpen | ArrayOpen | Dot) { @@ -255,7 +257,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(left) } - fn unit_expression(&mut self) -> Result<Expression> { + fn unit_expression(&mut self) -> Result<Expression, RHError> { if let Some(token) = self.tokens.peek() { match token.kind { Int(_) | Float(_) | Str(_) | KeywordTrue | KeywordFalse => { @@ -275,21 +277,47 @@ impl<T: Iterator<Item = Token>> Parser<T> { KeywordFn => Ok(Expression::FnLiteral(Box::new(self.function()?))), KeywordIf => Ok(Expression::If(Box::new(self.conditional()?))), KeywordLoop => Ok(Expression::Loop(Box::new(self.repeating()?))), - _ => Err(anyhow!("Unexpected token: {:?}", token.kind)), + _ => Err(parser_error( + ErrorLocation::Specific(token.location), + ParserError::UnexpectedToken { + received: token.kind.clone(), + expected: merge_token_names!( + Int(_), + Float(_), + Str(_), + KeywordTrue, + KeywordFalse, + Ident(_), + StrOpen, + GroupOpen, + BlockOpen, + ArrayOpen, + KeywordFn, + KeywordIf, + KeywordLoop + ), + }, + )), } } else { - Err(anyhow!("Expected expression.")) + Err(parser_error( + ErrorLocation::Eof, + ParserError::UnexpectedEof { + // Well sure this works. + expected: "expression".into(), + }, + )) } } - fn group(&mut self) -> Result<Expression> { + fn group(&mut self) -> Result<Expression, RHError> { consume!(self, GroupOpen)?; let expression = self.expression()?; consume!(self, GroupClose)?; Ok(expression) } - fn array(&mut self) -> Result<ArrayNode> { + fn array(&mut self) -> Result<ArrayNode, RHError> { consume!(self, ArrayOpen)?; let mut elements = Vec::new(); @@ -305,7 +333,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(ArrayNode { elements }) } - fn str(&mut self) -> Result<StrNode> { + fn str(&mut self) -> Result<StrNode, RHError> { let mut parts = Vec::new(); consume!(self, StrOpen)?; @@ -333,7 +361,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(StrNode { parts }) } - fn function(&mut self) -> Result<FnNode> { + fn function(&mut self) -> Result<FnNode, RHError> { consume!(self, KeywordFn)?; let header = { @@ -382,7 +410,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(FnNode { header, body }) } - fn conditional(&mut self) -> Result<IfNode> { + fn conditional(&mut self) -> Result<IfNode, RHError> { consume!(self, KeywordIf)?; let mut conditionals = Vec::new(); @@ -418,7 +446,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { }) } - fn repeating(&mut self) -> Result<LoopNode> { + fn repeating(&mut self) -> Result<LoopNode, RHError> { consume!(self, KeywordLoop)?; let condition = if consume_if!(self, KeywordIf).is_some() { @@ -432,7 +460,7 @@ impl<T: Iterator<Item = Token>> Parser<T> { Ok(LoopNode { body, condition }) } - fn generic_block(&mut self) -> Result<BlockNode> { + fn generic_block(&mut self) -> Result<BlockNode, RHError> { consume!(self, BlockOpen)?; let mut statements = Vec::new(); @@ -467,3 +495,22 @@ impl<T: Iterator<Item = Token>> Parser<T> { }) } } + +fn parser_error(at: ErrorLocation, parse_error: ParserError) -> RHError { + RHError { + at, + kind: RHErrorKind::Parse(parse_error), + } +} + +#[derive(Error, Debug)] +pub enum ParserError { + #[error("Received unexpected '{received}', expected: {expected}.'")] + UnexpectedToken { + received: TokenKind, + // Stringified expected tokens + expected: String, + }, + #[error("Unexpected end of file, expected: {expected}")] + UnexpectedEof { expected: String }, +} |
