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

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()))
}