fift 0.2.2

Rust implementation of the Fift esoteric language
Documentation
use anyhow::Result;
use tycho_vm::SafeRc;

use super::cont::{ContextTailWordFunc, ContextWordFunc, FiftCont, RcFiftCont, StackWordFunc};
use super::stack::{
    DynFiftValue, HashMapTreeKey, HashMapTreeKeyRef, HashMapTreeNode, SharedBox, StackValue,
    StackValueType,
};
use super::{DynFiftCont, IntoDynFiftCont, IntoDynFiftValue};

pub struct Dictionaries {
    pub current: Dictionary,
    pub original: Dictionary,
    pub context: Dictionary,
}

impl Default for Dictionaries {
    fn default() -> Self {
        let current = Dictionary::default();
        Self {
            original: current.clone(),
            context: current.clone(),
            current,
        }
    }
}

impl Dictionaries {
    pub fn lookup(&self, word: &String, allow_space: bool) -> Result<Option<DictionaryEntry>> {
        if allow_space {
            let mut entry = self.lookup(word, false)?;

            if entry.is_none() {
                entry = self.lookup(&format!("{word} "), false)?;
            }

            return Ok(entry);
        }

        let mut entry = self.context.lookup(word)?;

        if entry.is_none() && self.current != self.context {
            entry = self.current.lookup(word)?;
        }

        if entry.is_none() && self.original != self.context && self.original != self.current {
            entry = self.original.lookup(word)?;
        }

        Ok(entry)
    }
}

#[derive(Default, Clone, Eq, PartialEq)]
pub struct Dictionary {
    words: SafeRc<SharedBox>,
}

impl Dictionary {
    pub fn set_words_box(&mut self, words: SafeRc<SharedBox>) {
        self.words = words;
    }

    pub fn get_words_box(&self) -> &SafeRc<SharedBox> {
        &self.words
    }

    pub fn clone_words_map(&self) -> Result<Option<SafeRc<HashMapTreeNode>>> {
        let words = self.words.fetch();
        Ok(match words.ty() {
            StackValueType::Null => None,
            _ => Some(words.into_hashmap()?),
        })
    }

    pub fn use_words_map(&mut self) -> Result<WordsRefMut<'_>> {
        let words = self.words.take();
        let map = if words.is_null() {
            None
        } else {
            Some(words.into_hashmap()?)
        };
        Ok(WordsRefMut {
            words_box: &mut self.words,
            map,
        })
    }

    pub fn lookup(&self, name: &String) -> Result<Option<DictionaryEntry>> {
        let map = self.clone_words_map()?;
        let key = HashMapTreeKeyRef::from(name);
        let Some(node) = HashMapTreeNode::lookup(&map, key) else {
            return Ok(None);
        };
        Ok(DictionaryEntry::try_from_value(node.value.clone()))
    }

    pub fn resolve_name(&self, definition: &dyn FiftCont) -> Option<SafeRc<String>> {
        let map = self.words.borrow();
        if let Ok(map) = map.as_hashmap() {
            for entry in map {
                let Some((cont, _)) = DictionaryEntry::cont_ref_from_value(&*entry.value) else {
                    continue;
                };
                if std::ptr::addr_eq(cont, definition) {
                    return entry.key.stack_value.clone().into_string().ok();
                }
            }
        }
        None
    }

    pub fn define_context_word<T: Into<String>>(
        &mut self,
        name: T,
        f: ContextWordFunc,
    ) -> Result<()> {
        self.define_word(name, DictionaryEntry {
            definition: SafeRc::new_dyn_fift_cont(f),
            active: false,
        })
    }

    pub fn define_context_tail_word<T: Into<String>>(
        &mut self,
        name: T,
        f: ContextTailWordFunc,
    ) -> Result<()> {
        self.define_word(name, DictionaryEntry {
            definition: SafeRc::new_dyn_fift_cont(f),
            active: false,
        })
    }

    pub fn define_active_word<T: Into<String>>(
        &mut self,
        name: T,
        f: ContextWordFunc,
    ) -> Result<()> {
        self.define_word(name, DictionaryEntry {
            definition: SafeRc::new_dyn_fift_cont(f),
            active: true,
        })
    }

    pub fn define_stack_word<T: Into<String>>(&mut self, name: T, f: StackWordFunc) -> Result<()> {
        self.define_word(name, DictionaryEntry {
            definition: SafeRc::new_dyn_fift_cont(f),
            active: false,
        })
    }

    pub fn define_word<T, E>(&mut self, name: T, word: E) -> Result<()>
    where
        T: Into<String>,
        E: Into<DictionaryEntry>,
    {
        fn define_word_impl(d: &mut Dictionary, name: String, word: DictionaryEntry) -> Result<()> {
            let mut map = d.use_words_map()?;

            let key = HashMapTreeKey::from(name);
            let value = &word.into();
            HashMapTreeNode::set(&mut map, &key, value);
            Ok(())
        }
        define_word_impl(self, name.into(), word.into())
    }

    pub fn undefine_word(&mut self, name: &String) -> Result<bool> {
        let mut map = self.use_words_map()?;

        let key = HashMapTreeKeyRef::from(name);
        Ok(HashMapTreeNode::remove(&mut map, key).is_some())
    }
}

pub struct DictionaryEntry {
    pub definition: RcFiftCont,
    pub active: bool,
}

impl DictionaryEntry {
    fn try_from_value(value: SafeRc<dyn StackValue>) -> Option<Self> {
        let (definition, active) = Self::cont_from_value(value)?;
        Some(Self { definition, active })
    }

    fn cont_from_value(value: SafeRc<dyn StackValue>) -> Option<(RcFiftCont, bool)> {
        if value.ty() == StackValueType::Cont {
            return Some((value.into_cont().unwrap(), false));
        } else if let Ok(tuple) = value.as_tuple()
            && tuple.len() == 1
            && let Ok(cont) = tuple.first().cloned()?.into_cont()
        {
            return Some((cont, true));
        }
        None
    }

    fn cont_ref_from_value(value: &dyn StackValue) -> Option<(&dyn FiftCont, bool)> {
        if let Ok(value) = value.as_cont() {
            return Some((value, false));
        } else if let Ok(tuple) = value.as_tuple()
            && tuple.len() == 1
            && let Ok(cont) = tuple.first()?.as_cont()
        {
            return Some((cont, true));
        }
        None
    }
}

impl From<RcFiftCont> for DictionaryEntry {
    fn from(value: RcFiftCont) -> Self {
        Self {
            definition: value,
            active: false,
        }
    }
}

impl<T: FiftCont + 'static> From<SafeRc<T>> for DictionaryEntry {
    fn from(value: SafeRc<T>) -> Self {
        Self {
            definition: value.into_dyn_fift_cont(),
            active: false,
        }
    }
}

impl From<DictionaryEntry> for SafeRc<dyn StackValue> {
    fn from(value: DictionaryEntry) -> Self {
        let cont = value.definition.into_dyn_fift_value();
        if value.active {
            SafeRc::new_dyn_fift_value(vec![cont])
        } else {
            cont
        }
    }
}

pub struct WordsRefMut<'a> {
    words_box: &'a mut SafeRc<SharedBox>,
    map: Option<SafeRc<HashMapTreeNode>>,
}

impl std::ops::Deref for WordsRefMut<'_> {
    type Target = Option<SafeRc<HashMapTreeNode>>;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.map
    }
}

impl std::ops::DerefMut for WordsRefMut<'_> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.map
    }
}

impl Drop for WordsRefMut<'_> {
    fn drop(&mut self) {
        self.words_box.store_opt(self.map.take());
    }
}