use std::fmt::Display; use std::rc::Rc; use rlox2_frontend::lexer::LexerError; use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp}; use thiserror::Error; use crate::Value; use crate::{LoxClass, ResolverError}; #[derive(Error, Debug)] pub enum RuntimeError { #[error("unary operator {op} had invalid argument {arg}")] UnaryOpInvalidArgument { op: UnaryOp, arg: Value }, #[error("binary operator {op} had invalid arguments {left} and {right}")] BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value, }, #[error("division by zero")] DivisionByZero, #[error("local {name} is not defined")] NameNotDefined { name: String }, #[error("global {name} is not defined")] GlobalNotDefined { name: String }, #[error("{callee} is not callable")] NotCallable { callee: Value }, #[error("{name}() takes {arity} args, but {given} were given.")] WrongArity { name: String, arity: usize, given: usize, }, #[error("extern function call to {name} failed: {msg}")] ExtFunCallFailed { name: String, msg: String }, #[error("uncaught break statement")] Break, #[error("uncaught return statement")] Return { value: Value }, #[error("exit with exit code {exit_code}")] Exit { exit_code: i32 }, #[error("only objects have attributes")] InvalidGetTarget, #[error("only objects have attributes")] InvalidSetTarget, #[error("class {0} has no property {name}", class.name())] UndefinedAttribute { class: Rc, name: Box }, #[error("{value} is not a valid superclass")] InvalidSuperclass { value: Value }, #[error("stack overflow")] StackOverflow, } #[derive(Debug)] #[allow(clippy::enum_variant_names)] pub enum LoxError { LexerError { lexer_errors: Vec }, ParserError { parser_errors: Vec }, ResolverError { resolver_error: ResolverError }, RuntimeError { runtime_error: RuntimeError }, Exit { exit_code: i32 }, } impl Display for LoxError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let plural = |len: usize| { if len > 1 { "s" } else { "" } }; match self { LoxError::LexerError { lexer_errors } => { let len = lexer_errors.len(); writeln!(f, "{len} lexer error{}:", plural(len))?; if len > 1 { for lexer_error in lexer_errors[..len - 2].iter() { writeln!(f, "{lexer_error}")?; } } write!(f, "{}", lexer_errors[len - 1]) } LoxError::ParserError { parser_errors } => { let len = parser_errors.len(); writeln!(f, "{len} parser error{}:", plural(len))?; if len > 1 { for lexer_error in parser_errors[..len - 2].iter() { writeln!(f, "{lexer_error}")?; } } write!(f, "{}", parser_errors[len - 1]) } LoxError::ResolverError { resolver_error } => { write!(f, "resolver error: {resolver_error}") } LoxError::RuntimeError { runtime_error } => { write!(f, "runtime error: {runtime_error}") } LoxError::Exit { exit_code } => { write!(f, "exited with code {exit_code}") } } } } impl std::error::Error for LoxError {} impl From> for LoxError { fn from(lexer_errs: Vec) -> Self { LoxError::LexerError { lexer_errors: lexer_errs, } } } impl From> for LoxError { fn from(parser_errs: Vec) -> Self { LoxError::ParserError { parser_errors: parser_errs, } } } impl From for LoxError { fn from(resolver_err: ResolverError) -> Self { LoxError::ResolverError { resolver_error: resolver_err, } } } impl From for LoxError { fn from(runtime_err: RuntimeError) -> Self { match runtime_err { RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code }, _ => LoxError::RuntimeError { runtime_error: runtime_err, }, } } }