use std::error::Error;
use std::fmt::{Display, Formatter, Result as FResult};
use nom::error::{VerboseError, VerboseErrorKind};
use crate::scanner::{Span, Tokens};
use crate::spec::{Language, TextRange};
pub(crate) fn convert_parser_error(input: Tokens, e: VerboseError<Tokens>) -> String {
use std::fmt::Write;
let mut result = String::new();
for (i, (tokens, kind)) in e.errors.iter().enumerate() {
match kind {
VerboseErrorKind::Char(c) => {
if tokens.tok.is_empty() {
if let Some(mismatch) = input.tok.last() {
let mismatch = mismatch.inner();
let line = String::from_utf8(mismatch.get_line_beginning().to_vec()).unwrap();
let line_number = mismatch.location_line();
let column_number = mismatch.get_column() + 1;
write!(
&mut result,
"{i}: at line {line_number}:\n\n{line}\n{caret:>column$}\nexpected '{expected}', but encountered EOF\n\n",
i = i,
line_number = line_number,
line = line,
caret = '^',
column = column_number,
expected = c,
)
.unwrap();
} else {
write!(&mut result, "{i}: expected '{c}', but EOF\n\n",).unwrap();
}
} else {
let mismatch = tokens.tok[0].inner();
let line = String::from_utf8(mismatch.get_line_beginning().to_vec()).unwrap();
let line_number = mismatch.location_line();
let column_number = mismatch.get_column();
let actual = mismatch.fragment();
write!(
&mut result,
"{i}: at line {line_number}:\n\n{line}\n{caret:>column$}\nexpected '{expected}', found '{actual}'\n\n",
i = i,
line_number = line_number,
line = line,
caret = '^',
column = column_number,
expected = c,
actual = actual,
)
.unwrap();
}
},
VerboseErrorKind::Nom(nom::error::ErrorKind::Tag) => {
let mismatch = tokens.tok[0].inner();
let line = String::from_utf8(mismatch.get_line_beginning().to_vec()).unwrap();
let line_number = mismatch.location_line();
let column_number = mismatch.get_column();
let actual = mismatch.fragment();
write!(
&mut result,
"{i}: at line {line_number}:\n{line}\n{caret:>column$}\nunexpected token '{actual}'\n\n",
i = i,
line_number = line_number,
line = line,
caret = '^',
column = column_number,
actual = actual,
)
.unwrap();
},
VerboseErrorKind::Context(s) => {
let mismatch = tokens.tok[0].inner();
let line = String::from_utf8(mismatch.get_line_beginning().to_vec()).unwrap();
writeln!(result, "{i} in section '{s}', at: {line}").unwrap()
},
e => {
writeln!(result, "Compiler error: unkown error from parser: {e:?}").unwrap();
},
}
}
result
}
pub(crate) fn convert_scanner_error(input: Span, e: VerboseError<Span>) -> String {
use std::fmt::Write;
use nom::Offset;
let mut result = String::new();
for (i, (substring, kind)) in e.errors.iter().enumerate() {
let offset = input.offset(substring);
if input.is_empty() {
match kind {
VerboseErrorKind::Char(c) => write!(&mut result, "{i}: expected '{c}', got empty input\n\n"),
VerboseErrorKind::Context(s) => write!(&mut result, "{i}: in {s}, got empty input\n\n"),
VerboseErrorKind::Nom(e) => write!(&mut result, "{i}: in {e:?}, got empty input\n\n"),
}
} else {
let prefix = &input.as_bytes()[..offset];
let line_number = prefix.iter().filter(|&&b| b == b'\n').count() + 1;
let line_begin = prefix
.iter()
.rev()
.position(|&b| b == b'\n')
.map(|pos| offset - pos)
.unwrap_or(0);
let line = input[line_begin..]
.lines()
.next()
.unwrap_or(&input[line_begin..])
.trim_end();
let column_number = line.offset(substring) + 1;
match kind {
VerboseErrorKind::Char(c) => {
if let Some(actual) = substring.chars().next() {
write!(
&mut result,
"{i}: at line {line_number}:\n\
{line}\n\
{caret:>column$}\n\
expected '{expected}', found {actual}\n\n",
i = i,
line_number = line_number,
line = line,
caret = '^',
column = column_number,
expected = c,
actual = actual,
)
} else {
write!(
&mut result,
"{i}: at line {line_number}:\n\
{line}\n\
{caret:>column$}\n\
expected '{expected}', got end of input\n\n",
i = i,
line_number = line_number,
line = line,
caret = '^',
column = column_number,
expected = c,
)
}
}
VerboseErrorKind::Context(s) => write!(
&mut result,
"{i}: at line {line_number}, in {context}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
context = s,
line = line,
caret = '^',
column = column_number,
),
VerboseErrorKind::Nom(e) => write!(
&mut result,
"{i}: at line {line_number}, in {nom_err:?}:\n\
{line}\n\
{caret:>column$}\n\n",
i = i,
line_number = line_number,
nom_err = e,
line = line,
caret = '^',
column = column_number,
),
}
}
.unwrap();
}
result
}
#[derive(Debug)]
pub enum SymbolTableError {
DuplicateFunction { name: String, existing: TextRange, got: TextRange },
DuplicateClass { name: String, existing: TextRange, got: TextRange },
DuplicateVariable { name: String, existing: TextRange, got: TextRange },
DuplicateField { c_name: String, name: String, existing: TextRange, got: TextRange },
}
impl Display for SymbolTableError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use SymbolTableError::*;
match self {
DuplicateFunction { name, .. } => write!(f, "Duplicate definition of function '{name}'"),
DuplicateClass { name, .. } => write!(f, "Duplicate definition of class '{name}'"),
DuplicateVariable { name, .. } => write!(f, "Duplicate definition of variable '{name}'"),
DuplicateField { c_name, name, .. } => write!(f, "Duplicate definition of field '{name}' in class '{c_name}'"),
}
}
}
impl Error for SymbolTableError {}
#[derive(Debug)]
pub enum LanguageParseError {
UnknownLanguageId { raw: String },
}
impl Display for LanguageParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use LanguageParseError::*;
match self {
UnknownLanguageId { raw } => write!(f, "Unknown language ID '{raw}'"),
}
}
}
impl Error for LanguageParseError {}
#[derive(Debug)]
pub enum PatternError {
UnknownPattern { raw: String, range: TextRange },
}
impl Display for PatternError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use PatternError::*;
match self {
UnknownPattern { raw, .. } => write!(f, "Pattern '{raw}' is unknown (are you missing a package import?)"),
}
}
}
impl Error for PatternError {}
#[derive(Debug)]
pub enum ParseError {
ScanError { err: String },
ScannerError { err: String },
LeftoverSourceError,
ParseError { lang: Language, err: String },
ParserError { lang: Language, err: String },
Eof { lang: Language, err: String },
LeftoverTokensError { lang: Language },
}
impl Display for ParseError {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> FResult {
use self::ParseError::*;
match self {
ScanError { err } => write!(f, "Syntax error: {err}"),
ScannerError { err } => write!(f, "Syntax error: {err}"),
LeftoverSourceError => write!(f, "Syntax error: not all input could be parsed"),
ParseError { lang, err } => write!(f, "{lang} parse error: {err}"),
ParserError { lang, err } => write!(f, "{lang} parse error: {err}"),
Eof { lang, err } => write!(f, "{lang} parse error: reached end-of-file unexpectedly ({err})"),
LeftoverTokensError { lang } => write!(f, "{lang} parse error: not all input could be parsed"),
}
}
}
impl Error for ParseError {}