Use codespan for *much* prettier error reporting.

This commit is contained in:
2022-02-16 20:46:13 -08:00
parent 60e7d9a41d
commit 0293eee2d0
7 changed files with 222 additions and 137 deletions

View File

@@ -2,111 +2,44 @@ use lalrpop_util::lalrpop_mod;
mod token_stream;
mod tokens;
lalrpop_mod!(parser, "/syntax/parser.rs");
lalrpop_mod!(
#[allow(clippy::just_underscores_and_digits)]
parser,
"/syntax/parser.rs"
);
mod ast;
pub use crate::syntax::ast::*;
use crate::syntax::parser::ProgramParser;
use crate::syntax::token_stream::{LexerError, Location, TokenStream};
use crate::syntax::tokens::Token;
#[cfg(test)]
use crate::util::istring::InternedString;
use crate::syntax::token_stream::TokenStream;
pub use crate::syntax::token_stream::{LexerError, Location};
pub use crate::syntax::tokens::Token;
use lalrpop_util::ParseError;
use std::fmt;
use std::fs;
use std::io;
#[cfg(test)]
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ParserError {
IOError(io::Error),
ParseError(ParseError<Location, Token, LexerError>),
}
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParserError::IOError(e) => write!(f, "IO error: {}", e),
ParserError::ParseError(ParseError::ExtraToken{ token: (loc, tok, _)}) => {
write!(f, "{}: Unexpected additional token ({}) found at end of file", loc, tok)
}
ParserError::ParseError(ParseError::InvalidToken { location }) => {
write!(f, "{}: Unexpected token encountered", location)
}
ParserError::ParseError(ParseError::UnrecognizedEOF { location, expected }) => {
write!(f, "{}: Unexpected EOF{}", location, display_expected(expected))
}
ParserError::ParseError(ParseError::UnrecognizedToken { token: (location, tok, _), expected }) => {
write!(f, "{}: Unexpected token {}{}", location, tok, display_expected(expected))
}
ParserError::ParseError(ParseError::User{ error }) => {
write!(f, "{}: Couldn't process input (lexer error)", error.location)
}
}
}
}
fn display_expected(expected: &Vec<String>) -> String {
match expected.len() {
0 => "".to_string(),
1 => format!("; expected {}", expected[0]),
2 => format!("; expected {} or {}", expected[0], expected[1]),
n => format!("; expected {}or {}", comma_separate(&expected[0..n-1]), expected[n-1])
}
}
fn comma_separate(strings: &[String]) -> String {
let mut result = String::new();
for s in strings.iter() {
result.push_str(&s);
result.push_str(", ");
}
result
}
impl From<io::Error> for ParserError {
fn from(x: io::Error) -> Self {
ParserError::IOError(x)
}
}
impl From<ParseError<Location, Token, LexerError>> for ParserError {
fn from(x: ParseError<Location, Token, LexerError>) -> Self {
ParserError::ParseError(x)
}
}
type ParserError = ParseError<Location, Token, LexerError>;
impl Program {
pub fn from_file(filename: &str) -> Result<Program, ParserError> {
let metadata = fs::metadata(filename)?;
let mut buffer = String::with_capacity(metadata.len() as usize);
let lexer = TokenStream::from_file(filename, &mut buffer)?;
Ok(ProgramParser::new().parse(lexer)?)
}
fn parse(filename: &str, buffer: &mut String) -> Result<Program, ParserError> {
let lexer = TokenStream::new(filename, buffer);
Ok(ProgramParser::new().parse(lexer)?)
pub fn parse(file_idx: usize, buffer: &str) -> Result<Program, ParserError> {
let lexer = TokenStream::new(file_idx, buffer);
ProgramParser::new().parse(lexer)
}
}
#[cfg(test)]
impl FromStr for Program {
type Err = ParserError;
fn from_str(s: &str) -> Result<Program, ParserError> {
let mut s2 = s.to_string();
Program::parse("<from_str>", &mut s2)
Program::parse(0, s)
}
}
#[test]
fn order_of_operations() {
let muladd1 = "1 + 2 * 3";
let testfile = InternedString::new("<from_str>");
let testfile = 0;
assert_eq!(
Program::from_str(muladd1).unwrap(),
Program {