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(())
}
}