You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
214 lines
7.4 KiB
214 lines
7.4 KiB
use std::fmt::{self, Display, Write};
|
|
|
|
use crate::{
|
|
compiler::CompilerError,
|
|
parser::{Location, ParseError},
|
|
};
|
|
|
|
pub fn print_error<E: FormattedLocatedError>(err: E, src: Option<&str>) {
|
|
let mut buf = String::new();
|
|
match src {
|
|
Some(src) => {
|
|
err.fmt_with_src(&mut buf, &src).unwrap();
|
|
}
|
|
None => err.fmt(&mut buf).unwrap(),
|
|
}
|
|
print!("{buf}");
|
|
}
|
|
|
|
pub trait FormattedLocatedError {
|
|
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result;
|
|
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result;
|
|
}
|
|
|
|
impl Display for Location {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Location::Char { line, col } => write!(f, "{line}:{col}"),
|
|
Location::String { lines, cols } => write!(f, "{}:{}", lines.start(), cols.start()),
|
|
Location::All => write!(f, "Entire file"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FormattedLocatedError for ParseError {
|
|
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
|
|
match self {
|
|
ParseError::UnexpectedParenClose(location) => {
|
|
writeln!(w, "Unexpected closing paren at {location}")?
|
|
}
|
|
ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?,
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result {
|
|
match self {
|
|
ParseError::UnexpectedParenClose(location) => {
|
|
writeln!(w, "Unexpected closing paren at {location}")?;
|
|
here(location, src, Some("Unexpected ')'".to_string()), w)?;
|
|
}
|
|
ParseError::UnexpectedEof => writeln!(w, "Unexpected end of file")?,
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl FormattedLocatedError for CompilerError {
|
|
fn fmt<W: fmt::Write>(&self, w: &mut W) -> fmt::Result {
|
|
match self {
|
|
CompilerError::ExpectedSymbol(location) => {
|
|
writeln!(
|
|
w,
|
|
"Expected a symbol but got a list or nothing at {location}"
|
|
)
|
|
}
|
|
CompilerError::ExpectedList(location) => {
|
|
writeln!(
|
|
w,
|
|
"Expected a list but got a symbol or nothing at {location}"
|
|
)
|
|
}
|
|
CompilerError::UnknownKeyword(k, location) => {
|
|
writeln!(w, "Got unexpected keyword {k} at {location}")?;
|
|
writeln!(w, "Expected one of: module, import, bind, dec, entry, fun")
|
|
}
|
|
CompilerError::ExpectedOneOf(vec, k, location) => {
|
|
writeln!(w, "Got unexpected token {k} at {location}")?;
|
|
writeln!(w, "Expected one of: {}", vec.join(", "))
|
|
}
|
|
CompilerError::TrailingTokens(location) => {
|
|
writeln!(w, "Got unexpected trailing tokens at {location}")?;
|
|
writeln!(w, "Expected end of list")
|
|
}
|
|
CompilerError::UnknownBind(b, location) => {
|
|
writeln!(w, "Got unexpected keyword {b} at {location}")?;
|
|
writeln!(w, "Expected one of: BuiltIn, Location")
|
|
}
|
|
CompilerError::ExpectedType(location) => {
|
|
writeln!(w, "Expected name:type but got name at {location}")
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fmt_with_src<W: fmt::Write>(&self, w: &mut W, src: &str) -> fmt::Result {
|
|
match self {
|
|
CompilerError::ExpectedSymbol(location) => {
|
|
writeln!(
|
|
w,
|
|
"Expected a symbol but got a list or nothing at {location}"
|
|
)?;
|
|
here(location, src, Some("Expected symbol".to_string()), w)
|
|
}
|
|
CompilerError::ExpectedList(location) => {
|
|
writeln!(
|
|
w,
|
|
"Expected a list but got a symbol or nothing at {location}"
|
|
)?;
|
|
here(location, src, Some("Expected List".to_string()), w)
|
|
}
|
|
CompilerError::UnknownKeyword(k, location) => {
|
|
writeln!(w, "Got unexpected keyword {k} at {location}")?;
|
|
writeln!(w, "Expected one of: module, import, bind, dec, entry, fun")?;
|
|
here(location, src, Some("Unexpected keyword".to_string()), w)
|
|
}
|
|
CompilerError::ExpectedOneOf(vec, k, location) => {
|
|
writeln!(w, "Got unexpected token {k} at {location}")?;
|
|
writeln!(w, "Expected one of: {}", vec.join(", "))?;
|
|
here(location, src, Some("Unexpected token".to_string()), w)
|
|
}
|
|
CompilerError::TrailingTokens(location) => {
|
|
writeln!(w, "Got unexpected trailing tokens at {location}")?;
|
|
writeln!(w, "Expected end of list")?;
|
|
here(location, src, Some("Unexpected token".to_string()), w)
|
|
}
|
|
CompilerError::UnknownBind(b, location) => {
|
|
writeln!(w, "Got unexpected keyword {b} at {location}")?;
|
|
writeln!(w, "Expected one of: BuiltIn, Location")?;
|
|
here(location, src, Some("Unexpected keyword".to_string()), w)
|
|
}
|
|
CompilerError::ExpectedType(location) => {
|
|
writeln!(w, "Expected name:type but got name at {location}")?;
|
|
here(location, src, None, w)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn here<W: fmt::Write>(
|
|
loc: &Location,
|
|
src: &str,
|
|
comment: Option<String>,
|
|
w: &mut W,
|
|
) -> fmt::Result {
|
|
writeln!(w, "Here:")?;
|
|
writeln!(
|
|
w,
|
|
"{}",
|
|
annotate_src(&loc, src, comment.unwrap_or_default())
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn annotate_src(loc: &Location, src: &str, comment: String) -> String {
|
|
let src_lines = src.split('\n').collect::<Vec<_>>();
|
|
|
|
let mut buf = String::new();
|
|
|
|
match loc {
|
|
Location::Char { line, col } => {
|
|
writeln!(buf, "{}|{}", line_num(*line - 1), src_lines[*line - 2]).unwrap();
|
|
writeln!(buf, "{}|{}", line_num(*line), src_lines[*line - 1]).unwrap();
|
|
writeln!(buf, "{}|{}^ {}", " ", " ".repeat(col - 1), comment).unwrap();
|
|
writeln!(buf, "{}|{}", line_num(*line + 1), src_lines[*line]).unwrap();
|
|
}
|
|
Location::String { lines, cols } => {
|
|
writeln!(
|
|
buf,
|
|
"{}|{}",
|
|
line_num(*lines.start() - 1),
|
|
src_lines[*lines.start() - 2]
|
|
)
|
|
.unwrap();
|
|
writeln!(
|
|
buf,
|
|
"{}",
|
|
src_lines[(lines.start() - 1)..*lines.end()]
|
|
.iter()
|
|
.zip(lines.clone())
|
|
.map(|(l, n)| format!("{}|{}", line_num(n), l))
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
)
|
|
.unwrap();
|
|
writeln!(
|
|
buf,
|
|
"{}|{}{} {}",
|
|
" ",
|
|
" ".repeat(cols.start() - 1),
|
|
"^".repeat(cols.end() - cols.start() + 1),
|
|
comment
|
|
)
|
|
.unwrap();
|
|
writeln!(
|
|
buf,
|
|
"{}|{}",
|
|
line_num(*lines.end() + 1),
|
|
src_lines[*lines.end()]
|
|
)
|
|
.unwrap();
|
|
}
|
|
Location::All => todo!(),
|
|
}
|
|
|
|
buf
|
|
}
|
|
|
|
fn line_num(num: usize) -> String {
|
|
let num = format!("{num}");
|
|
format!("{num}{}", " ".repeat(6 - num.len()))
|
|
}
|