fift 0.2.2

Rust implementation of the Fift esoteric language
Documentation
use std::num::NonZeroU32;

use anyhow::{Context as _, Result};
pub use fift_proc::fift_module;
use tycho_vm::SafeRc;

pub use self::cont::{DynFiftCont, FiftCont, IntoDynFiftCont, RcFiftCont};
pub use self::dictionary::{Dictionaries, Dictionary, DictionaryEntry};
pub use self::env::{Environment, SourceBlock};
pub use self::lexer::Lexer;
pub use self::stack::{
    Atom, DynFiftValue, HashMapTreeKey, HashMapTreeNode, IntoDynFiftValue, OwnedCellSlice,
    SharedBox, Stack, StackTuple, StackValue, StackValueType, WordList,
};

pub mod cont;
pub mod dictionary;
pub mod env;
pub mod lexer;
pub mod stack;

pub struct Context<'a> {
    pub state: State,
    pub stack: Stack,
    pub exit_code: u8,
    pub next: Option<RcFiftCont>,
    pub dicts: Dictionaries,

    pub limits: ExecutionLimits,
    pub stats: ExecutionStats,

    pub input: Lexer,
    pub exit_interpret: SharedBox,

    pub env: &'a mut dyn Environment,
    pub stdout: &'a mut dyn std::io::Write,
    pub stderr: &'a mut dyn std::fmt::Write,
}

impl<'a> Context<'a> {
    pub fn new(
        env: &'a mut dyn Environment,
        stdout: &'a mut dyn std::io::Write,
        stderr: &'a mut dyn std::fmt::Write,
    ) -> Self {
        Self {
            state: Default::default(),
            stack: Stack::new(None),
            exit_code: 0,
            next: None,
            dicts: Default::default(),
            limits: Default::default(),
            stats: Default::default(),
            input: Default::default(),
            exit_interpret: Default::default(),
            env,
            stdout,
            stderr,
        }
    }

    pub fn with_module<T: Module>(mut self, module: T) -> Result<Self> {
        self.add_module(module)?;
        Ok(self)
    }

    pub fn add_module<T: Module>(&mut self, module: T) -> Result<()> {
        module.init(&mut self.dicts.current)
    }

    pub fn with_source_block(mut self, block: SourceBlock) -> Self {
        self.add_source_block(block);
        self
    }

    pub fn add_source_block(&mut self, block: SourceBlock) {
        self.input.push_source_block(block);
    }

    pub fn with_limits(mut self, limits: ExecutionLimits) -> Self {
        self.set_limits(limits);
        self
    }

    pub fn set_limits(&mut self, limits: ExecutionLimits) {
        self.limits = limits;
    }

    pub fn run(&mut self) -> Result<u8> {
        self.stats = Default::default();
        let mut current = Some(RcFiftCont::new_dyn_fift_cont(cont::InterpreterCont));
        while let Some(cont) = current.take() {
            self.stats.inc_step(&self.limits)?;
            current = SafeRc::into_inner(cont).run(self)?;
            if current.is_none() {
                current = self.next.take();
            }
        }

        Ok(self.exit_code)
    }

    pub(crate) fn execute_stack_top(&mut self) -> Result<RcFiftCont> {
        let cont = self.stack.pop_cont()?;
        let count = self.stack.pop_smallint_range(0, 255)? as usize;
        self.stack.check_underflow(count)?;
        Ok(cont)
    }

    pub(crate) fn compile_stack_top(&mut self) -> Result<()> {
        let word_def = self.stack.pop_cont()?;
        let count = self.stack.pop_smallint_range(0, 255)? as usize;

        let cont = match count {
            0 => None,
            1 => Some(RcFiftCont::new_dyn_fift_cont(cont::LitCont(
                self.stack.pop()?,
            ))),
            _ => {
                let mut literals = Vec::with_capacity(count);
                for _ in 0..count {
                    literals.push(self.stack.pop()?);
                }
                literals.reverse();
                Some(RcFiftCont::new_dyn_fift_cont(cont::MultiLitCont(literals)))
            }
        };

        let mut word_list = self.stack.pop_word_list()?;
        {
            let word_list = SafeRc::make_mut(&mut word_list);
            word_list.items.extend(cont);

            if !cont::NopCont::is_nop(word_def.as_ref()) {
                word_list.items.push(SafeRc::clone(&word_def));
            }
        }

        self.stack.push_raw(word_list.into_dyn_fift_value())
    }
}

#[derive(Debug, Default)]
pub enum State {
    #[default]
    Interpret,
    Compile(NonZeroU32),
    InterpretInternal(NonZeroU32),
}

impl State {
    pub fn is_compile(&self) -> bool {
        matches!(self, Self::Compile(_))
    }

    pub fn begin_compile(&mut self) -> Result<()> {
        match self {
            Self::Interpret => {
                *self = Self::Compile(NonZeroU32::MIN);
                Ok(())
            }
            Self::Compile(depth) => {
                *depth = depth.checked_add(1).context("Compiler depth overflow")?;
                Ok(())
            }
            Self::InterpretInternal(_) => anyhow::bail!("Expected non-internal interpreter mode"),
        }
    }

    pub fn end_compile(&mut self) -> Result<()> {
        if let Self::Compile(depth) = self {
            match NonZeroU32::new(depth.get() - 1) {
                Some(new_depth) => *depth = new_depth,
                None => *self = Self::Interpret,
            }
            Ok(())
        } else {
            anyhow::bail!("Expected compilation mode")
        }
    }

    pub fn begin_interpret_internal(&mut self) -> Result<()> {
        if let Self::Compile(depth) = self {
            *self = Self::InterpretInternal(*depth);
            Ok(())
        } else {
            anyhow::bail!("Expected compilation mode")
        }
    }

    pub fn end_interpret_internal(&mut self) -> Result<()> {
        if let Self::InterpretInternal(depth) = self {
            *self = Self::Compile(*depth);
            Ok(())
        } else {
            anyhow::bail!("Expected internal interpreter mode")
        }
    }
}

pub trait Module {
    fn init(&self, d: &mut Dictionary) -> Result<()>;
}

impl<T: Module> Module for &T {
    fn init(&self, d: &mut Dictionary) -> Result<()> {
        T::init(self, d)
    }
}

#[derive(Debug, Default, Clone)]
pub struct ExecutionLimits {
    pub max_steps: Option<usize>,
    pub max_include_depth: Option<u16>,
}

#[derive(Debug, Default, Clone)]
pub struct ExecutionStats {
    pub step: usize,
}

impl ExecutionStats {
    pub fn inc_step(&mut self, limits: &ExecutionLimits) -> Result<()> {
        self.step += 1;
        if let Some(max_steps) = limits.max_steps {
            anyhow::ensure!(
                self.step <= max_steps,
                "Max execution steps exceeded: {max_steps}/{max_steps}"
            );
        }
        Ok(())
    }
}