Skip to content
Snippets Groups Projects
mod.rs 5.36 KiB
Newer Older
Louis's avatar
Louis committed
use crate::lexer::Span;
use crate::parse::ScriptToken;
use peg::error::ExpectedSet;
Louis's avatar
Louis committed
use std::error::Error;
use std::fmt::{Display, Formatter};

#[derive(Debug)]
pub enum TokenErrorKind<'a> {
	Incomplete,
	NomError(nom::error::Error<Span<'a>>),
}

#[derive(Debug)]
pub struct TokenError<'a> {
	pub kind: TokenErrorKind<'a>,
}

impl<'a> From<TokenErrorKind<'a>> for TokenError<'a> {
	fn from(value: TokenErrorKind<'a>) -> Self {
		TokenError { kind: value }
	}
}

impl<'a> Display for TokenError<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		match &self.kind {
			TokenErrorKind::Incomplete => write!(f, "Incomplete Program"),
			TokenErrorKind::NomError(err) => write!(f, "{}", err),
		}
	}
}
impl<'a> Error for TokenError<'a> {}
impl<'a> From<nom::Err<nom::error::Error<Span<'a>>>> for TokenError<'a> {
	fn from(value: nom::Err<nom::error::Error<Span<'a>>>) -> Self {
		match value {
			nom::Err::Error(err) => TokenErrorKind::NomError(err).into(),
			nom::Err::Failure(err) => TokenErrorKind::NomError(err).into(),
			nom::Err::Incomplete(_) => TokenErrorKind::Incomplete.into(),
		}
	}
}

#[derive(Clone, Debug)]
pub enum ParseErrorKind<'a> {
	Unexpected {
		found: ScriptToken<'a>,
		expected: peg::error::ExpectedSet,
	},
}

#[derive(Clone, Debug)]
pub struct ParseError<'a> {
	pub kind: ParseErrorKind<'a>,
}

#[derive(Debug)]
pub enum ForgeErrorKind<'a> {
	IncompleteInput,
	LexerError(nom::error::Error<Span<'a>>),
	UnexpectedToken {
		found: ScriptToken<'a>,
		expected: peg::error::ExpectedSet,
	},
}

#[derive(Debug)]
pub struct ForgeError<'a> {
	pub kind: ForgeErrorKind<'a>,
}

impl<'a> From<ParseError<'a>> for ForgeError<'a> {
	fn from(value: ParseError<'a>) -> Self {
		match value.kind {
			ParseErrorKind::Unexpected { found, expected } => ForgeError {
				kind: ForgeErrorKind::UnexpectedToken { found, expected },
			},
		}
	}
}

impl<'a> From<TokenError<'a>> for ForgeError<'a> {
	fn from(value: TokenError<'a>) -> Self {
		match value.kind {
			TokenErrorKind::Incomplete => ForgeError {
				kind: ForgeErrorKind::IncompleteInput,
			},
			TokenErrorKind::NomError(span) => ForgeError {
				kind: ForgeErrorKind::LexerError(span),
			},
		}
	}
}

pub type ForgeResult<'a, T> = Result<T, ForgeError<'a>>;

pub fn print_unexpected_token<'a>(
	source: &'a str,
	token: &'a ScriptToken<'a>,
	expected: &'a peg::error::ExpectedSet,
) {
	let line = token.position.location_line() as usize;
	let column = token.position.get_column();

	let previous_line = if line > 1 {
		source.lines().nth(line - 2)
	} else {
		None
	};
	let source_line = source.lines().nth(line - 1).expect("Missing line");
	let next_line = source.lines().nth(line);

	let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1));
	let number_length = format!("{}", largest_line_num).len();

	eprintln!("| Script error on line {} at \"{}\"\n|", line, token);
	if let Some(prev) = previous_line {
		eprintln!("| [{:>width$}] {}", line - 1, prev, width = number_length);
	}
	eprintln!(
		"| [{:>width$}] {}",
		line,
		source_line,
		width = number_length
	);
	eprintln!(
		"| {} {}{}",
		vec![" "; number_length + 2].join(""),
		vec![" "; column - 1].join(""),
		vec!["^"; token.token_type.len()].join(""),
	);
	if let Some(next) = next_line {
		eprintln!("| [{:>width$}] {}", line + 1, next, width = number_length);
	}
	eprintln!("|\n| Failed To Parse: expected {}", expected);
}

pub struct ErrorFormat<'a>(pub &'a str, pub &'a ScriptToken<'a>, pub &'a ExpectedSet);

impl<'a> Display for ErrorFormat<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		let ErrorFormat(source, token, expected) = self;

		let line = token.position.location_line() as usize;
		let column = token.position.get_column();

		let previous_line = if line > 1 {
			source.lines().nth(line - 2)
		} else {
			None
		};
		let source_line = source.lines().nth(line - 1).expect("Missing line");
		let next_line = source.lines().nth(line);

		let largest_line_num = line.max(line.saturating_sub(1)).max(line.saturating_add(1));
		let number_length = format!("{}", largest_line_num).len();

		writeln!(f, "| Script error on line {} at \"{}\"\n|", line, token)?;
		if let Some(prev) = previous_line {
			writeln!(
				f,
				"| [{:>width$}] {}",
				line - 1,
				prev,
				width = number_length
			)?;
		}
		writeln!(
			f,
			"| [{:>width$}] {}",
			line,
			source_line,
			width = number_length
		)?;
		writeln!(
			f,
			"| {} {}{}",
			vec![" "; number_length + 2].join(""),
			vec![" "; column - 1].join(""),
			vec!["^"; token.token_type.len()].join(""),
		)?;
		if let Some(next) = next_line {
			writeln!(
				f,
				"| [{:>width$}] {}",
				line + 1,
				next,
				width = number_length
			)?;
		}
		writeln!(f, "|\n| Failed To Parse: expected {}", expected)
	}
}

Louis's avatar
Louis committed
pub fn print_forge_error<'a>(source: &'a str, fe: &'a ForgeError) {
	match &fe.kind {
		ForgeErrorKind::IncompleteInput => eprintln!("| Unexpected end of file"),
		ForgeErrorKind::LexerError(err) => eprintln!("| {}", err),
		ForgeErrorKind::UnexpectedToken { found, expected } => {
			print_unexpected_token(source, found, expected)
		}
	}
}

pub fn format_forge_error<'a>(source: &'a str, fe: &'a ForgeError) -> String {
	match &fe.kind {
		ForgeErrorKind::IncompleteInput => String::from("| Unexpected end of file"),
		ForgeErrorKind::LexerError(err) => format!("| {}", err),
		ForgeErrorKind::UnexpectedToken { found, expected } => {
			format!("{}", ErrorFormat(source, found, expected))
		}
	}
}