use std::rc::Rc; use itertools::Itertools; 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("RuntimeError: Unary operator {op} had invalid argument {arg}")] UnaryOpInvalidArgument { op: UnaryOp, arg: Value }, #[error("RuntimeError: Binary operator {op} had invalid arguments {left} and {right}")] BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value, }, #[error("RuntimeError: Division by zero")] DivisionByZero, #[error("RuntimeError: Local {name} is not defined")] NameNotDefined { name: String }, #[error("RuntimeError: Global {name} is not defined")] GlobalNotDefined { name: String }, #[error("RuntimeError: {callee} is not callable")] NotCallable { callee: Value }, #[error("RuntimeError: {name}() takes {arity} args, but {given} were given.")] WrongArity { name: String, arity: usize, given: usize, }, #[error("RuntimeError: Extern function call to {name} failed: {msg}")] ExtFunCallFailed { name: String, msg: String }, #[error("RuntimeError: Uncaught break statement")] Break, #[error("RuntimeError: Uncaught return statement")] Return { value: Value }, #[error("RuntimeError: Exit with exit code {exit_code}")] Exit { exit_code: i32 }, #[error("RuntimeError: Only objects have attributes")] InvalidGetTarget, #[error("RuntimeError: Only objects have attributes")] InvalidSetTarget, #[error("RuntimeError: Class {0} has no property {name}", class.name())] UndefinedAttribute { class: Rc, name: Box }, #[error("RuntimeError: {value} is not a valid superclass")] InvalidSuperclass { value: Value }, } #[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: &[impl std::error::Error]) -> 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 }, } } }