ralix 0.2.0

A simple, type-safe, tree walking interpreter
use std::{error::Error, fmt::Display, io};

use crate::{
    Expression, Literal, Value,
    expressions::{Identifier, InfixOperator, PrefixOperator},
    types::Type,
};

#[derive(Debug)]
pub enum RuntimeError {
    Undefined(Literal),
    UnsupportedCopyType(Type),

    UnsupportedInfixOperation(Type, InfixOperator, Type),
    UnsupportedPrefixOperation(PrefixOperator, Type),
    UnsupportedIndexOperation(Type, Type),

    CannotBeDereferenced(Type),
    CannotAssign(Expression, Value),
    IsNotAFunction(Type),

    ImportCycleDetected(Identifier),
    ModuleExecuteError(io::Error),
}

#[derive(Debug)]
pub enum EvalResult<T> {
    Value(T),
    Return(Option<T>),
    Err(RuntimeError),
    NoValue,
}

impl Error for RuntimeError {}
impl Display for RuntimeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use RuntimeError as E;

        f.write_str(&match self {
            E::Undefined(lit) => format!("`{lit}` is undefined"),
            E::UnsupportedCopyType(ty) => format!("Cannot create a copy of type `{ty}`"),
            E::UnsupportedInfixOperation(left_ty, op, right_ty) => {
                format!("Operator `{op}` is not supported for types `{left_ty}` and `{right_ty}`")
            }
            E::UnsupportedIndexOperation(left, idx) => {
                format!("Index operation not supported for types `{left}` and `{idx}`")
            }
            E::UnsupportedPrefixOperation(op, right_ty) => {
                format!("Operator `{op}` is not supported for type `{right_ty}`")
            }
            E::CannotBeDereferenced(t) => format!("Type `{t}` cannot be dereferenced"),
            E::CannotAssign(left, value) => {
                format!("Cannot assign value {value} using {left} expression")
            }
            E::IsNotAFunction(t) => {
                format!("Value type `{t}` is not a function and cannot be called")
            }
            E::ImportCycleDetected(m_name) => {
                format!("Module `{m_name}` re-imported in another module.")
            }
            E::ModuleExecuteError(m_err) => m_err.to_string(),
        })
    }
}