Skip to content

Commit

Permalink
Improve more parser errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivorforce committed Apr 26, 2024
1 parent 030535a commit cf9b620
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 61 deletions.
42 changes: 3 additions & 39 deletions src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,21 @@
use itertools::Itertools;
use lalrpop_util::{ErrorRecovery, ParseError};
use lalrpop_util::ErrorRecovery;

use crate::error::{RResult, RuntimeError};
use crate::error::RResult;
use crate::monoteny_grammar;
use crate::parser::error::Error;
use crate::parser::lexer::Token;

pub mod ast;
pub mod strings;
pub mod lexer;
pub mod error;
mod tests;

fn rem_first_and_last(value: &str) -> &str {
let mut chars = value.chars();
chars.next();
chars.next_back();
chars.as_str()
}

pub fn parse_program(content: &str) -> RResult<(ast::Block, Vec<ErrorRecovery<usize, lexer::Token<'_>, error::Error>>)> {
let lexer = lexer::Lexer::new(content);
let mut errors = vec![];
let ast = monoteny_grammar::BlockParser::new()
.parse(&mut errors, content, lexer)
.map_err(|e| {
match e {
ParseError::InvalidToken { location } => {
RuntimeError::error("Invalid token.").in_range(location..location)
},
ParseError::UnrecognizedEof { location, expected } => {
RuntimeError::error("File ended unexpectedly.").in_range(location..location)
.with_note(make_expected_note(expected))
}
ParseError::UnrecognizedToken { token: (start, token, end), expected } => {
RuntimeError::error("Unrecognized token.").in_range(start..end)
.with_note(make_expected_note(expected))
}
ParseError::ExtraToken { token: (start, token, end) } => {
RuntimeError::error("Extra token.").in_range(start..end)
}
ParseError::User { error } => {
panic!()
}
}.to_array()
})?;
.map_err(|e| { error::map_parse_error(&e).to_array() })?;

Ok((ast, errors))
}

fn make_expected_note(expected: Vec<String>) -> RuntimeError {
match &expected[..] {
[one] => RuntimeError::note(format!("Expected: {}", rem_first_and_last(one)).as_str()),
expected => RuntimeError::note(format!("Expected one of: {}", expected.iter().map(|s| rem_first_and_last(s)).join(" ")).as_str()),
}
}
62 changes: 40 additions & 22 deletions src/parser/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use annotate_snippets::Level;
use itertools::Itertools;
use lalrpop_util::{ErrorRecovery, ParseError};

use crate::error::RuntimeError;
Expand All @@ -8,27 +8,45 @@ use crate::parser::lexer::Token;
pub struct Error(pub String);

pub fn derive_error(error: &ErrorRecovery<usize, Token<'_>, Error>, start: usize, end: usize) -> RuntimeError {
RuntimeError {
level: Level::Error,
path: None,
range: Some(start..end),
title: match &error.error {
ParseError::InvalidToken { .. } => {
format!("Invalid token")
}
ParseError::UnrecognizedEof { .. } => {
format!("Unexpected end of file")
}
ParseError::UnrecognizedToken { token, expected } => {
format!("Unexpected token: {}", token.1)
}
ParseError::ExtraToken { token } => {
format!("Extraneous token: {}", token.1)
}
ParseError::User { error } => {
format!("{}", error.0)
}
map_parse_error(&error.error)
}

pub fn map_parse_error(e: &ParseError<usize, Token, Error>) -> RuntimeError {
match e {
ParseError::InvalidToken { location } => {
RuntimeError::error("Invalid token.").in_range(*location..*location)
},
notes: vec![],
ParseError::UnrecognizedEof { location, expected } => {
RuntimeError::error("File ended unexpectedly.").in_range(*location..*location)
.with_note(make_expected_note(expected))
}
ParseError::UnrecognizedToken { token: (start, token, end), expected } => {
RuntimeError::error("Unrecognized token.").in_range(*start..*end)
.with_note(make_expected_note(expected))
}
ParseError::ExtraToken { token: (start, token, end) } => {
RuntimeError::error("Extra token.").in_range(*start..*end)
}
ParseError::User { error } => {
panic!()
}
}
}

fn unquote(value: &str) -> &str {
if !value.starts_with('\"') {
return value
}

let mut chars = value.chars();
chars.next();
chars.next_back();
chars.as_str()
}

fn make_expected_note(expected: &Vec<String>) -> RuntimeError {
match &expected[..] {
[one] => RuntimeError::note(format!("Expected: {}", unquote(one)).as_str()),
expected => RuntimeError::note(format!("Expected one of: {}", expected.iter().map(|s| unquote(s)).join(" ")).as_str()),
}
}

0 comments on commit cf9b620

Please sign in to comment.