Skip to content
Snippets Groups Projects

Resolve "Better Error Locations"

Merged loutr requested to merge 38-error-locations into main
3 files
+ 97
90
Compare changes
  • Side-by-side
  • Inline
Files
3
+ 13
89
//! Parser for [`Command`]s.
use kernel::memory::declaration::builder as declaration;
use kernel::memory::level::builder as level;
use kernel::memory::term::builder as term;
use pest::error::LineColLocation;
use pest::iterators::Pair;
use pest::{Parser, Span};
use utils::location::Location;
use crate::command::Command;
use crate::error;
use crate::error::Error;
/// [`Command`]'s parser generated by pest.
#[derive(Parser)]
#[grammar = "command/grammar.pest"]
struct CommandParser;
/// Convert pest location [`Span`] to our location [`Location`].
/// Convert pest's [`Span`] to our location [`Location`](utils::location::Location).
fn convert_span(span: Span) -> Location {
let (x1, y1) = span.start_pos().line_col();
let (x2, y2) = span.end_pos().line_col();
@@ -22,7 +23,7 @@ fn convert_span(span: Span) -> Location {
Location::new((x1, y1), (x2, y2))
}
/// build universe level from errorless pest's output
/// Build [`kernel`]'s [`Level`](level::Builder) from errorless pest's output
fn parse_level(pair: Pair<Rule>) -> level::Builder {
use level::Builder::{Const, IMax, Max, Plus, Var};
@@ -62,7 +63,7 @@ fn parse_level(pair: Pair<Rule>) -> level::Builder {
}
}
/// Returns a kernel term builder from pest output
/// Build [`kernel`]'s [`Term`](term::Builder) from errorless pest's output
fn parse_term(pair: Pair<Rule>) -> term::Builder {
use term::Builder;
use term::Payload::{Abs, App, Decl, Prod, Prop, Sort, Type, Var};
@@ -138,7 +139,7 @@ fn parse_term(pair: Pair<Rule>) -> term::Builder {
}
}
/// build commands from errorless pest's output
/// Build [`Command`] from errorless pest's output
fn parse_expr(pair: Pair<Rule>) -> Command {
match pair.as_rule() {
Rule::GetType => {
@@ -220,102 +221,25 @@ fn parse_expr(pair: Pair<Rule>) -> Command {
}
}
/// convert pest error to parser error
fn convert_error(err: pest::error::Error<Rule>) -> error::Error {
// renaming error messages
let err = err.renamed_rules(|rule| match *rule {
Rule::string | Rule::Var => "variable".to_owned(),
Rule::number => "number".to_owned(),
Rule::Define => "def var := term".to_owned(),
Rule::Declaration => "def decl.{ vars, ... } := term".to_owned(),
Rule::DeclarationCheckType => "def decl.{ vars, ... } : term := term".to_owned(),
Rule::CheckType => "check term : term".to_owned(),
Rule::GetType => "check term".to_owned(),
Rule::DefineCheckType => "def var : term := term".to_owned(),
Rule::Abs => "abstraction".to_owned(),
Rule::dProd => "dependent product".to_owned(),
Rule::Prod => "product".to_owned(),
Rule::App => "application".to_owned(),
Rule::Prop => "Prop".to_owned(),
Rule::Type => "Type".to_owned(),
Rule::Sort => "Sort".to_owned(),
Rule::Eval => "eval term".to_owned(),
Rule::filename => "path_to_file".to_owned(),
Rule::ImportFile => "import path_to_file".to_owned(),
Rule::Search => "search var".to_owned(),
Rule::Max => "max".to_owned(),
Rule::Plus => "plus".to_owned(),
Rule::IMax => "imax".to_owned(),
Rule::arg_univ => "universe argument".to_owned(),
Rule::univ_decl => "universe declaration".to_owned(),
_ => {
unreachable!("low level rules cannot appear in error messages")
},
});
// extracting the location from the pest output
let loc = match err.line_col {
LineColLocation::Pos((x, y)) => {
let mut right = y;
let mut left = 1;
let chars = err.line().chars();
let mut i = 0;
for c in chars {
i += 1;
if char::is_whitespace(c) {
if i < y {
left = i + 1;
} else {
break;
}
} else {
right = i;
}
}
if i < y {
left = y;
right = y;
}
Location::new((x, left), (x, right))
},
LineColLocation::Span(start, end) => Location::new(start, end),
};
// extracting the message from the pest output
let message = err.to_string();
let mut chars = message.lines().next_back().unwrap().chars();
(0_i32..4_i32).for_each(|_| {
chars.next();
});
Error {
kind: error::Kind::CannotParse(chars.as_str().to_owned()),
loc,
}
}
/// Parse a text input and try to convert it into a command.
///
/// if unsuccessful, a box containing the first error that was encountered is returned.
/// # Errors
/// If unsuccessful, the first error that was encountered is returned.
#[inline]
pub fn line(line: &str) -> error::Result<Command> {
CommandParser::parse(Rule::command, line)
.map_err(convert_error)
.map_err(std::convert::Into::into)
.map(|mut pairs| parse_expr(pairs.next().unwrap_or_else(|| unreachable!())))
}
/// Parse a text input and try to convert it into a vector of commands.
///
/// if unsuccessful, a box containing the first error that was encountered is returned.
/// # Errors
/// If unsuccessful, the first error that was encountered is returned.
#[inline]
pub fn file(file: &str) -> error::Result<Vec<Command>> {
CommandParser::parse(Rule::file, file)
.map_err(convert_error)
.map_err(std::convert::Into::into)
.map(|pairs| pairs.into_iter().map(parse_expr).collect())
}
Loading