scala 0.1.1

A experimental Scala interpreter written in Rust: lexer, parser, type inference, and tree-walking evaluation with a REPL.
Documentation
use std::fmt;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub enum Value {
    Int(i64),
    Long(i64),
    Double(f64),
    Float(f64),
    Bool(bool),
    Char(char),
    String(String),
    Unit,
    Null,
    Tuple(Vec<Value>),
    List(Vec<Value>),
    Map(Vec<(Value, Value)>),
    Function {
        name: Option<String>,
        params: Vec<String>,
        body: crate::ast::Expr,
        closure: Vec<(String, Value, bool)>,
    },
    BuiltinFunction {
        name: String,
        arity: usize,
    },
    Object {
        class_name: String,
        fields: HashMap<String, Value>,
        methods: HashMap<String, Value>,
    },
    Array(Vec<Value>),
    Nothing,
}

impl Value {
    pub fn type_name(&self) -> &'static str {
        match self {
            Value::Int(_) => "Int",
            Value::Long(_) => "Long",
            Value::Double(_) => "Double",
            Value::Float(_) => "Float",
            Value::Bool(_) => "Boolean",
            Value::Char(_) => "Char",
            Value::String(_) => "String",
            Value::Unit => "Unit",
            Value::Null => "Null",
            Value::Tuple(_) => "Tuple",
            Value::List(_) => "List",
            Value::Map(_) => "Map",
            Value::Function { .. } => "Function",
            Value::BuiltinFunction { .. } => "Function",
            Value::Object { .. } => "Object",
            Value::Array(_) => "Array",
            Value::Nothing => "Nothing",
        }
    }

    pub fn is_truthy(&self) -> bool {
        match self {
            Value::Bool(b) => *b,
            Value::Null => false,
            Value::Int(n) => *n != 0,
            Value::Unit => false,
            _ => true,
        }
    }

    pub fn to_int(&self) -> Option<i64> {
        match self {
            Value::Int(n) => Some(*n),
            Value::Long(n) => Some(*n),
            Value::Double(n) => Some(*n as i64),
            Value::Float(n) => Some(*n as i64),
            Value::Char(c) => Some(*c as i64),
            Value::Bool(b) => Some(if *b { 1 } else { 0 }),
            _ => None,
        }
    }

    pub fn to_double(&self) -> Option<f64> {
        match self {
            Value::Int(n) => Some(*n as f64),
            Value::Long(n) => Some(*n as f64),
            Value::Double(n) => Some(*n),
            Value::Float(n) => Some(*n as f64),
            _ => None,
        }
    }

    pub fn deep_clone(&self) -> Value {
        self.clone()
    }
}

impl PartialEq for Value {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Value::Int(a), Value::Int(b)) => a == b,
            (Value::Long(a), Value::Long(b)) => a == b,
            (Value::Double(a), Value::Double(b)) => a == b,
            (Value::Float(a), Value::Float(b)) => a == b,
            (Value::Bool(a), Value::Bool(b)) => a == b,
            (Value::Char(a), Value::Char(b)) => a == b,
            (Value::String(a), Value::String(b)) => a == b,
            (Value::Unit, Value::Unit) => true,
            (Value::Null, Value::Null) => true,
            (Value::Tuple(a), Value::Tuple(b)) => a == b,
            (Value::List(a), Value::List(b)) => a == b,
            (Value::Int(a), Value::Long(b)) => a == b,
            (Value::Long(a), Value::Int(b)) => a == b,
            _ => false,
        }
    }
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Value::Int(v) => write!(f, "{}", v),
            Value::Long(v) => write!(f, "{}", v),
            Value::Double(v) => {
                if v.fract() == 0.0 {
                    write!(f, "{:.1}", v)
                } else {
                    write!(f, "{}", v)
                }
            }
            Value::Float(v) => write!(f, "{}", v),
            Value::Bool(v) => write!(f, "{}", v),
            Value::Char(v) => write!(f, "{}", v),
            Value::String(v) => write!(f, "{}", v),
            Value::Unit => write!(f, "()"),
            Value::Null => write!(f, "null"),
            Value::Tuple(elements) => {
                write!(f, "(")?;
                for (i, e) in elements.iter().enumerate() {
                    if i > 0 { write!(f, ",")?; }
                    write!(f, "{}", e)?;
                }
                write!(f, ")")
            }
            Value::List(elements) => {
                write!(f, "List(")?;
                for (i, e) in elements.iter().enumerate() {
                    if i > 0 { write!(f, ", ")?; }
                    write!(f, "{}", e)?;
                }
                write!(f, ")")
            }
            Value::Map(entries) => {
                write!(f, "Map(")?;
                for (i, (k, v)) in entries.iter().enumerate() {
                    if i > 0 { write!(f, ", ")?; }
                    write!(f, "{} -> {}", k, v)?;
                }
                write!(f, ")")
            }
            Value::Function { name, .. } => {
                match name {
                    Some(n) => write!(f, "<function {}>", n),
                    None => write!(f, "<function>"),
                }
            }
            Value::BuiltinFunction { name, .. } => write!(f, "<builtin {}>", name),
            Value::Object { class_name, fields, .. } => {
                write!(f, "{}(", class_name)?;
                let field_list: Vec<String> = fields.iter()
                    .map(|(k, v)| format!("{} = {}", k, v))
                    .collect();
                write!(f, "{})", field_list.join(", "))
            }
            Value::Array(elements) => {
                write!(f, "Array(")?;
                for (i, e) in elements.iter().enumerate() {
                    if i > 0 { write!(f, ", ")?; }
                    write!(f, "{}", e)?;
                }
                write!(f, ")")
            }
            Value::Nothing => write!(f, "<nothing>"),
        }
    }
}