loalang 0.1.15

Loa is a general-purpose, purely immutable, object-oriented programming language.
Documentation
use crate::syntax::characters_to_string;
use crate::vm::*;
use crate::*;
use std::f64::INFINITY;
use std::ptr::null;

pub static mut STRING_CLASS: *const Class = null();
pub static mut CHARACTER_CLASS: *const Class = null();
pub static mut SYMBOL_CLASS: *const Class = null();

pub static mut U8_CLASS: *const Class = null();
pub static mut U16_CLASS: *const Class = null();
pub static mut U32_CLASS: *const Class = null();
pub static mut U64_CLASS: *const Class = null();
pub static mut U128_CLASS: *const Class = null();
pub static mut UBIG_CLASS: *const Class = null();
pub static mut I8_CLASS: *const Class = null();
pub static mut I16_CLASS: *const Class = null();
pub static mut I32_CLASS: *const Class = null();
pub static mut I64_CLASS: *const Class = null();
pub static mut I128_CLASS: *const Class = null();
pub static mut IBIG_CLASS: *const Class = null();
pub static mut F32_CLASS: *const Class = null();
pub static mut F64_CLASS: *const Class = null();
pub static mut FBIG_CLASS: *const Class = null();

pub static mut TRUE: *const Arc<Object> = null();
pub static mut FALSE: *const Arc<Object> = null();

#[derive(Debug)]
pub struct Object {
    pub class: Option<Arc<Class>>,
    pub const_value: ConstValue,
}

impl PartialEq for Object {
    fn eq(&self, other: &Object) -> bool {
        self.class
            .as_ref()
            .and_then(|lc| other.class.as_ref().map(|rc| Arc::ptr_eq(lc, rc)))
            .unwrap_or(false)
            && self.const_value == other.const_value
    }
}

impl Object {
    pub fn new(class: &Arc<Class>) -> Arc<Object> {
        Arc::new(Object {
            class: Some(class.clone()),
            const_value: ConstValue::Nothing,
        })
    }

    pub fn get_variable(&self, variable: &Variable) -> Option<Arc<Object>> {
        match self.const_value {
            ConstValue::InstanceVariables(ref v) => v.get(&variable.id).cloned(),
            _ => None,
        }
    }

    pub fn set_variable(&self, variable: &Variable, value: Arc<Object>) -> Arc<Object> {
        Arc::new(Object {
            class: self.class.clone(),
            const_value: match self.const_value {
                ConstValue::InstanceVariables(ref v) => {
                    let mut v = v.clone();
                    v.insert(variable.id, value);
                    ConstValue::InstanceVariables(v)
                }
                _ => {
                    ConstValue::InstanceVariables(vec![(variable.id, value)].into_iter().collect())
                }
            },
        })
    }

    pub fn box_bool(value: bool) -> Arc<Object> {
        if value {
            unsafe { (*TRUE).clone() }
        } else {
            unsafe { (*FALSE).clone() }
        }
    }

    pub fn box_const(const_value: ConstValue, class_ptr: &mut *const Class) -> Arc<Object> {
        #[cfg(debug_assertions)]
        {
            if *class_ptr == null() {
                panic!("Tried to box {:?}, but class was not loaded.", const_value);
            }
        }
        let class = unsafe { Arc::from_raw(*class_ptr) };
        *class_ptr = Arc::into_raw(class.clone());
        Arc::new(Object {
            class: Some(class.clone()),
            const_value,
        })
    }

    pub fn lazy(offset: u64, call_stack: CallStack, dependencies: Vec<Arc<Object>>) -> Arc<Object> {
        Arc::new(Object {
            class: None,
            const_value: ConstValue::Lazy(offset, call_stack, dependencies),
        })
    }

    pub fn box_string(value: String) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { STRING_CLASS })
    }

    pub fn box_character(value: u16) -> Arc<Object> {
        Object::box_const(ConstValue::Character(value), &mut unsafe {
            CHARACTER_CLASS
        })
    }

    pub fn box_symbol(value: String) -> Arc<Object> {
        Object::box_const(ConstValue::Symbol(value), &mut unsafe { SYMBOL_CLASS })
    }

    pub fn box_u8(value: u8) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { U8_CLASS })
    }

    pub fn box_u16(value: u16) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { U16_CLASS })
    }

    pub fn box_u32(value: u32) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { U32_CLASS })
    }

    pub fn box_u64(value: u64) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { U64_CLASS })
    }

    pub fn box_u128(value: u128) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { U128_CLASS })
    }

    pub fn box_ubig(value: BigUint) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { UBIG_CLASS })
    }

    pub fn box_i8(value: i8) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { I8_CLASS })
    }

    pub fn box_i16(value: i16) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { I16_CLASS })
    }

    pub fn box_i32(value: i32) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { I32_CLASS })
    }

    pub fn box_i64(value: i64) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { I64_CLASS })
    }

    pub fn box_i128(value: i128) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { I128_CLASS })
    }

    pub fn box_ibig(value: BigInt) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { IBIG_CLASS })
    }

    pub fn box_f32(value: f32) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { F32_CLASS })
    }

    pub fn box_f64(value: f64) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { F64_CLASS })
    }

    pub fn box_fbig(value: BigFraction) -> Arc<Object> {
        Object::box_const(value.into(), &mut unsafe { FBIG_CLASS })
    }
}

impl fmt::Display for Object {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.const_value {
            ConstValue::Nothing => write!(
                f,
                "{}",
                self.class.as_ref().map(|c| c.name.as_ref()).unwrap_or("")
            ),
            ConstValue::InstanceVariables(ref v) => write!(
                f,
                "a {}({})",
                self.class.as_ref().map(|c| c.name.as_ref()).unwrap_or(""),
                {
                    let mut variables = std::collections::BTreeSet::new();

                    let class = self.class.as_ref().unwrap();
                    for (id, value) in v.iter() {
                        let mut var = class.variables.get(&id).unwrap().name.clone();
                        var.push('=');
                        var.push_str(format!("{}", value).as_ref());
                        variables.insert(var);
                    }

                    variables.into_iter().collect::<Vec<_>>().join(", ")
                }
            ),
            ConstValue::String(s) => write!(f, "{}", s),
            ConstValue::Lazy(_, _, _) => write!(f, "$lazy"),
            ConstValue::Character(c) => write!(f, "{}", characters_to_string([*c].iter().cloned())),
            ConstValue::Symbol(s) => write!(f, "#{}", s),
            ConstValue::U8(n) => write!(f, "{}", n),
            ConstValue::U16(n) => write!(f, "{}", n),
            ConstValue::U32(n) => write!(f, "{}", n),
            ConstValue::U64(n) => write!(f, "{}", n),
            ConstValue::U128(n) => write!(f, "{}", n),
            ConstValue::UBig(n) => write!(f, "{}", n),
            ConstValue::I8(n) => write!(f, "{}", n),
            ConstValue::I16(n) => write!(f, "{}", n),
            ConstValue::I32(n) => write!(f, "{}", n),
            ConstValue::I64(n) => write!(f, "{}", n),
            ConstValue::I128(n) => write!(f, "{}", n),
            ConstValue::IBig(n) => write!(f, "{}", n),
            ConstValue::F32(n) => write!(f, "{}", n),
            ConstValue::F64(n) => write!(f, "{}", n),
            ConstValue::FBig(n) => write!(f, "{:.1$}", n, INFINITY as usize),
        }
    }
}