prolangkit 0.1.16

A set of language-agnostic utilities for langdev
Documentation
use std::collections::{BTreeMap, HashMap};
use std::fmt::Debug;
use std::hash::Hash;
use std::marker::PhantomData;

pub use prolangkit_macros::*;

pub struct Id<T: ?Sized>(u32, PhantomData<fn() -> T>);

impl<T: ?Sized> Debug for Id<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("Id").field(&self.0).finish()
    }
}

impl<T: ?Sized> Clone for Id<T> {
    fn clone(&self) -> Self {
        *self
    }
}

impl<T: ?Sized> Copy for Id<T> {}

impl<T: ?Sized> Hash for Id<T> {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}

impl<T: ?Sized> PartialEq for Id<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<T: ?Sized> Eq for Id<T> {}

impl<T: ?Sized> PartialOrd for Id<T> {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl<T: ?Sized> Ord for Id<T> {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0)
    }
}

impl<T: ?Sized> Id<T> {
    pub fn new(index: usize) -> Self {
        Self(index.try_into().unwrap(), PhantomData)
    }

    pub fn index(self) -> usize {
        usize::try_from(self.0).unwrap()
    }
}

pub struct Arena<T> {
    values: Vec<T>,
}

impl<T> Default for Arena<T> {
    fn default() -> Self {
        Self { values: Default::default() }
    }
}

impl<T> Arena<T> {
    pub fn get(&self, id: Id<T>) -> &T {
        self.values.get(id.index()).unwrap()
    }

    pub fn get_mut(&mut self, id: Id<T>) -> &mut T {
        self.values.get_mut(id.index()).unwrap()
    }

    pub fn insert(&mut self, value: T) -> Id<T> {
        let id = Id::new(self.values.len());
        self.values.push(value);
        id
    }
}

pub struct Mapping<K, V> {
    values: HashMap<Id<K>, V>,
}

impl<K, V> Default for Mapping<K, V> {
    fn default() -> Self {
        Self { values: Default::default() }
    }
}

impl<K, V> Mapping<K, V> {
    pub fn get(&self, id: Id<K>) -> Option<&V> {
        self.values.get(&id)
    }

    pub fn get_mut(&mut self, id: Id<K>) -> Option<&mut V> {
        self.values.get_mut(&id)
    }

    pub fn insert(&mut self, id: Id<K>, value: V) -> &mut V {
        self.values.insert(id, value);
        self.values.get_mut(&id).unwrap()
    }
}

#[derive(Default)]
pub struct StringStore {
    lookup: BTreeMap<String, Id<str>>,
    ids: Vec<(usize, usize)>,
    strings: String,
}

impl StringStore {
    pub fn get(&self, id: Id<str>) -> &str {
        let (start, end) = self.ids.get(id.index()).copied().unwrap();
        &self.strings[start..end]
    }

    pub fn get_mut(&mut self, id: Id<str>) -> &mut str {
        let (start, end) = self.ids.get(id.index()).copied().unwrap();
        &mut self.strings[start..end]
    }

    pub fn insert(&mut self, value: &str) -> Id<str> {
        if let Some(id) = self.lookup.get(value) {
            return *id;
        }

        let start = self.strings.len();
        let end = start + value.len();
        self.strings.push_str(value);

        let id = Id::new(self.ids.len());
        self.ids.push((start, end));

        self.lookup.insert(value.to_string(), id);

        id
    }
}