use itertools::Itertools; use rlox2_frontend::lexer::LexerError; use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp}; use thiserror::Error; use crate::ResolverError; use crate::Value; #[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("Name {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 }, } #[derive(Error, Debug)] #[allow(clippy::enum_variant_names)] pub enum LoxError { #[error("{0}", format_multiple_errors(inner))] LexerError { inner: Vec }, #[error("{0}", format_multiple_errors(inner))] ParserError { inner: Vec }, #[error("{inner}")] ResolverError { inner: ResolverError }, #[error("{inner}")] RuntimeError { inner: RuntimeError }, #[error("Called exit() with exit code {exit_code}")] Exit { exit_code: i32 }, } fn format_multiple_errors(errs: &Vec) -> String { let msg = if errs.len() == 1 { errs[0].to_string() } else { errs.iter().map(|err| err.to_string()).join("\n") }; msg } impl From> for LoxError { fn from(lexer_errs: Vec) -> Self { LoxError::LexerError { inner: lexer_errs } } } impl From> for LoxError { fn from(parser_errs: Vec) -> Self { LoxError::ParserError { inner: parser_errs } } } impl From for LoxError { fn from(resolver_err: ResolverError) -> Self { LoxError::ResolverError { inner: 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 { inner: runtime_err }, } } }