dlang 0.1.3

Programing language from scratch
Documentation
pub mod environment;

use std::fmt::Debug;

use crate::{
    ast::{BlockStatement, Identifier, Nodetrait},
    bytecode_vm::bytecode::instructions::Instructions,
    utils::add_pad,
};

use self::environment::Environment;

#[derive(Debug, Clone, PartialEq)]
pub enum Object {
    Return(Return),
    Int(Int),
    Bool(Bool),
    String(StringObject),
    Function(Function),
    CompiledFunction(CompiledFunction),
    Array(Array),
    Closure(ClosureFunction),
}

#[derive(Debug, Clone, PartialEq)]
pub enum ObjectType {
    Return,
    Int,
    Bool,
    String,
    Function,
    CompiledFunction,
    Array,
    Closure,
}

pub trait ObjectTrait {
    fn get_type(&self) -> ObjectType;
    fn to_str(&self) -> String;
}

impl ObjectTrait for Object {
    fn get_type(&self) -> ObjectType {
        match self {
            Object::Return(x) => x.get_type(),
            Object::Int(x) => x.get_type(),
            Object::Bool(x) => x.get_type(),
            Object::Function(x) => x.get_type(),
            Object::String(x) => x.get_type(),
            Object::Array(x) => x.get_type(),
            Object::CompiledFunction(x) => x.get_type(),
            Object::Closure(x) => x.get_type(),
        }
    }

    fn to_str(&self) -> String {
        match self {
            Object::Return(x) => x.to_str(),
            Object::Int(x) => x.to_str(),
            Object::Bool(x) => x.to_str(),
            Object::String(x) => x.to_str(),
            Object::Function(x) => x.to_str(),
            Object::Array(x) => x.to_str(),
            Object::CompiledFunction(x) => x.to_str(),
            Object::Closure(x) => x.to_str(),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Return {
    pub value: Option<Box<Object>>,
}

impl ObjectTrait for Return {
    fn get_type(&self) -> ObjectType {
        ObjectType::Return
    }

    fn to_str(&self) -> String {
        format!("{:?}", self)
    }
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub struct Int {
    pub value: i64,
}
impl ObjectTrait for Int {
    fn get_type(&self) -> ObjectType {
        ObjectType::Int
    }
    fn to_str(&self) -> String {
        format!("Int:{}", self.value)
    }
}

#[derive(Debug, Clone, PartialEq, Copy)]
pub struct Bool {
    pub value: bool,
}
impl ObjectTrait for Bool {
    fn get_type(&self) -> ObjectType {
        ObjectType::Bool
    }
    fn to_str(&self) -> String {
        format!("Bool:{}", self.value)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct StringObject {
    pub value: String,
}
impl ObjectTrait for StringObject {
    fn get_type(&self) -> ObjectType {
        ObjectType::String
    }
    fn to_str(&self) -> String {
        format!("String:{}", self.value)
    }
}

#[derive(Debug, Clone)]
pub struct Function {
    pub identifier: Option<String>,
    pub args: Vec<Identifier>,
    pub block: BlockStatement,
    pub env: Environment<String>,
}

impl PartialEq for Function {
    fn eq(&self, other: &Self) -> bool {
        self.identifier == other.identifier
            && self.args == other.args
            && self.block == other.block
    }
}

impl ObjectTrait for Function {
    fn get_type(&self) -> ObjectType {
        ObjectType::Function
    }
    fn to_str(&self) -> String {
        let mut buf = String::new();
        if self.identifier.is_some() {
            buf += &format!("fn {}", &self.identifier.clone().unwrap());
        }
        let mut arguments = Vec::new();
        for arg in self.args.clone() {
            arguments.push(arg.to_str())
        }
        buf += "(";
        buf += &arguments.join(", ");
        buf += ") {\n";
        buf += &self.block.to_str();
        buf += "}";

        buf
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct Array {
    pub elements: Vec<Object>,
}

impl ObjectTrait for Array {
    fn get_type(&self) -> ObjectType {
        ObjectType::Array
    }
    fn to_str(&self) -> String {
        let mut buf = String::new();

        let mut elements_buf = Vec::new();
        for obj in self.elements.iter() {
            elements_buf.push(obj.to_str())
        }
        buf += "[";
        buf += &elements_buf.join(", ");
        buf += "]";

        buf
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct CompiledFunction {
    pub instructions: Instructions,
    pub local_len: usize,
    pub arg_len: usize,
}

impl ObjectTrait for CompiledFunction {
    fn get_type(&self) -> ObjectType {
        ObjectType::CompiledFunction
    }
    fn to_str(&self) -> String {
        let mut buf = String::new();
        buf += "CompiledFunction\n";
        buf += &format!(
            ">\tlocal_len={}, arg_len={}\n",
            self.local_len, self.arg_len
        );
        buf += ">\tFuntion's Instructions\n";
        buf += &add_pad(&self.instructions.to_string(), ">\t");
        buf += "\n";

        buf
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct ClosureFunction {
    pub fun: CompiledFunction,
    pub free: Vec<Object>,
}

impl ObjectTrait for ClosureFunction {
    fn get_type(&self) -> ObjectType {
        ObjectType::CompiledFunction
    }
    fn to_str(&self) -> String {
        let mut buf = String::new();
        buf += "Closure\n";
        buf += &format!(">Free variables, len:{}\n", self.free.len());
        for (idx, obj) in self.free.iter().enumerate() {
            buf += &format!("{idx}->{}\n", &obj.to_str());
        }
        buf += ">Inner Function\n";
        buf += &self.fun.to_str();

        buf
    }
}

pub fn is_same_type(left: &Object, right: &Object) -> bool {
    left.get_type() == right.get_type()
}