pr47 0.0.1

A semi-experimental programming language. Still working in progress.
Documentation
use std::any::TypeId;
use std::ptr::NonNull;
use std::collections::BTreeMap;
use std::sync::atomic::AtomicU8;
use std::convert::TryFrom;

use crate::error::Pr47Error;

pub trait Pr47DynBase {
    fn get_type_id(&self) -> TypeId;

    fn get_type_name(&self) -> &'static str;

    unsafe fn cast(&self,
                   dest_type_id: TypeId,
                   dest_type_name: &'static str) -> Result<NonNull<()>, Pr47Error> {
        if self.get_type_id() == dest_type_id {
            Ok(NonNull::new_unchecked(self as *const Self as *mut Self as *mut ()))
        } else {
            Err(Pr47Error::TypeCast {
                from: self.get_type_id(),
                to: dest_type_id,
                from_name: self.get_type_name(),
                to_name: dest_type_name
            })
        }
    }
}

type Pr47Object = BTreeMap<String, Box<dyn Pr47DynBase>>;

pub struct Pr47Wrapper<T> {
    pub data: T
}

impl<T> Pr47Wrapper<T> {
    pub fn new(data: T) -> Self {
        Self { data }
    }
}

impl Pr47DynBase for Pr47Object {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<Pr47Object>()
    }

    fn get_type_name(&self) -> &'static str {
        "Object"
    }
}

impl Pr47DynBase for () {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<()>()
    }

    fn get_type_name(&self) -> &'static str {
        "rust::Void"
    }
}

impl Pr47DynBase for i64 {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<i64>()
    }

    fn get_type_name(&self) -> &'static str {
        "int"
    }
}

impl Pr47DynBase for f64 {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<f64>()
    }

    fn get_type_name(&self) -> &'static str {
        "float"
    }
}

impl Pr47DynBase for String {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<String>()
    }

    fn get_type_name(&self) -> &'static str {
        "string"
    }
}

impl<T: 'static> Pr47DynBase for Pr47Wrapper<T> {
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<Pr47Wrapper<T>>()
    }

    fn get_type_name(&self) -> &'static str {
        std::any::type_name::<T>()
    }

    unsafe fn cast(&self,
                   dest_type_id: TypeId,
                   dest_type_name: &'static str) -> Result<NonNull<()>, Pr47Error> {
        if dest_type_id == self.get_type_id() {
            Ok(NonNull::new_unchecked(self as *const Self as *mut Self as *mut ()))
        } else if dest_type_id == TypeId::of::<T>() {
            Ok(NonNull::new_unchecked(&self.data as *const T as *mut T as *mut ()))
        } else {
            Err(Pr47Error::TypeCast {
                from: self.get_type_id(),
                to: dest_type_id,
                from_name: self.get_type_name(),
                to_name: dest_type_name
            })
        }
    }
}

#[derive(Copy, Clone)]
pub enum Pr47Value {
    ByteValue(u8),
    IntValue(i64),
    FloatValue(f64),
    BoolValue(bool),
    CharValue(char)
}

#[derive(Copy, Clone)]
pub enum GcInfo {
    Useful,
    Garbage,
    SharedWithHost,
    MutSharedWithHost,
    MovedToHost,
}

impl Into<u8> for GcInfo {
    fn into(self) -> u8 {
        match self {
            GcInfo::Useful => 0,
            GcInfo::Garbage => 1,
            GcInfo::SharedWithHost => 2,
            GcInfo::MutSharedWithHost => 3,
            GcInfo::MovedToHost => 4
        }
    }
}

impl From<u8> for GcInfo {
    fn from(num: u8) -> Self {
        match num {
            0 => GcInfo::Useful,
            1 => GcInfo::Garbage,
            2 => GcInfo::SharedWithHost,
            3 => GcInfo::MutSharedWithHost,
            4 => GcInfo::MovedToHost,
            _ => panic!()
        }
    }
}

#[derive(Copy, Clone)]
pub struct Pr47Ptr {
    pub data_ptr: Option<NonNull<dyn Pr47DynBase>>,
    pub gc_info_ptr: NonNull<AtomicU8>
}

impl Pr47Ptr {
    pub fn dangling() -> Self {
        Self {
            data_ptr: None,
            gc_info_ptr: NonNull::dangling()
        }
    }

    pub fn shared(data_ptr: &mut (dyn Pr47DynBase + 'static)) -> Self {
        Self {
            data_ptr: unsafe {
                Some(NonNull::new_unchecked(data_ptr as *mut dyn Pr47DynBase))
            },
            gc_info_ptr: unsafe {
                NonNull::new_unchecked(
                    Box::into_raw(
                        Box::new(
                            AtomicU8::new(
                                GcInfo::SharedWithHost.into()))))
            }
        }
    }

    pub fn owned<T: 'static + Pr47DynBase>(data: T) -> Self {
        Self {
            data_ptr: unsafe {
                Some(NonNull::new_unchecked(
                    Box::into_raw(Box::new(data)) as *mut dyn Pr47DynBase))
            },
            gc_info_ptr: unsafe {
                NonNull::new_unchecked(
                    Box::into_raw(
                        Box::new(
                            AtomicU8::new(
                                GcInfo::Useful.into()))))
            }
        }
    }

    pub fn from_raw(data: Box<dyn Pr47DynBase>) -> Self {
        Self {
            data_ptr: unsafe {
                Some(NonNull::new_unchecked(Box::into_raw(data)))
            },
            gc_info_ptr: unsafe {
                NonNull::new_unchecked(
                    Box::into_raw(
                        Box::new(
                            AtomicU8::new(
                                GcInfo::Useful.into()))))
            }
        }
    }

    pub unsafe fn explicit_drop_data(&mut self) {
        if let Some(data_ptr) = self.data_ptr {
            Box::from_raw(data_ptr.as_ptr());
        }
    }

    pub unsafe fn explicit_drop_gc(&mut self) {
        if self.data_ptr.is_some() {
            Box::from_raw(self.gc_info_ptr.as_ptr());
        }
    }
}

#[derive(Copy, Clone)]
pub struct Pr47PtrNonNull {
    pub data_ptr: NonNull<dyn Pr47DynBase>,
    pub gc_info_ptr: NonNull<AtomicU8>
}

impl TryFrom<&Pr47Ptr> for Pr47PtrNonNull {
    type Error = Pr47Error;

    fn try_from(value: &Pr47Ptr) -> Result<Self, Self::Error> {
        if let Some(data_ptr) = value.data_ptr.as_ref() {
            Ok(Self {
                data_ptr: data_ptr.clone(),
                gc_info_ptr: value.gc_info_ptr.clone()
            })
        } else {
            Err(Pr47Error::NullPointer)
        }
    }
}