ferth 0.2.0

A safe, native-sized Forth. no_std compatible.
Documentation
use crate::data::{Data, Mem};
use crate::error::{Error, Ior};
use crate::header::{Flags, Header, Info};
use crate::{Result, SIZE};

use super::layout::Layout;
use super::{INPUT_BUFFER_SIZE, MAX_WORD_LEN};

pub(crate) struct Dict<'a, M: Mem> {
    data: &'a mut Data<M>,
    base_addr: usize,
}

impl<'a, M: Mem> Dict<'a, M> {
    pub(crate) fn new(data: &'a mut Data<M>, base_addr: usize) -> Self {
        Self { data, base_addr }
    }

    pub(crate) fn addr(&self, offset: usize) -> usize {
        self.base_addr.wrapping_add(offset)
    }

    pub(crate) fn here(&self) -> Result<usize> {
        Ok(self.data.read_cell(self.layout_addr(Layout::HERE))?)
    }

    pub(crate) fn set_here(&mut self, addr: usize) -> Result<()> {
        Ok(self.data.write_cell(self.layout_addr(Layout::HERE), addr)?)
    }

    pub(crate) fn latest(&self) -> Result<usize> {
        Ok(self.data.read_cell(self.layout_addr(Layout::LATEST))?)
    }

    pub(crate) fn set_latest(&mut self, addr: usize) -> Result<()> {
        Ok(self
            .data
            .write_cell(self.layout_addr(Layout::LATEST), addr)?)
    }

    pub(crate) fn comma(&mut self, x: usize) -> Result<()> {
        let here = self.here()?;
        self.data.write_cell(here, x)?;
        self.set_here(here + SIZE)
    }

    fn header_at(&mut self, len: u8, flags: u8) -> Result<(usize, usize)> {
        let latest = self.latest()?;
        let here = self.here()?;
        let header = Header::from_name_len(here, len as usize);
        // name
        let name_addr = header.bodylen_addr() - len as usize - 1;
        self.data.write_char(name_addr, len)?;
        // bodylen: 0 until ; executes
        self.data.write_cell(header.bodylen_addr(), 0)?;
        // info
        let info = Info::new(flags.into(), len);
        self.data.write_cell(header.info_addr(), info.into())?;
        // link
        self.data.write_cell(header.link_addr(), latest)?;
        // code
        Ok((name_addr, header.code_addr()))
    }

    pub(crate) fn create(&mut self, name: &[u8], flags: u8) -> Result<usize> {
        let len: u8 = name
            .len()
            .try_into()
            .map_err(|_| Error::Throw(Ior::DEFINITION_NAME_TOO_LONG))?;
        let (name_addr, code_addr) = self.header_at(len, flags)?;
        self.data.write(name_addr + 1, name)?;
        Ok(code_addr)
    }

    pub(crate) fn create_at(&mut self, src_addr: usize, len: usize, flags: u8) -> Result<usize> {
        let len: u8 = len
            .try_into()
            .map_err(|_| Error::Throw(Ior::DEFINITION_NAME_TOO_LONG))?;
        let (name_addr, code_addr) = self.header_at(len, flags)?;
        self.data
            .copy_within(src_addr, name_addr + 1, len as usize)?;
        Ok(code_addr)
    }

    pub(crate) fn find_at(&self, addr: usize, len: usize) -> Result<Option<(usize, usize)>> {
        let name = self.data.read(addr, len)?;
        self.find(name)
    }

    pub(crate) fn find(&self, name: &[u8]) -> Result<Option<(usize, usize)>> {
        if name.len() > MAX_WORD_LEN {
            return Ok(None);
        }
        let mut xt = self.latest()?;
        while xt != 0 {
            let header = Header::new(xt);
            let info: Info = self.data.read_cell(header.info_addr())?.into();
            let flags = info.flags();
            let wlen = info.name_len();
            if !flags.contains(Flags::HIDDEN) && wlen == name.len() {
                let name_at = header.bodylen_addr() - wlen;
                let b = self.data.read(name_at, wlen)?;
                if name.eq_ignore_ascii_case(b) {
                    let flag = if flags.contains(Flags::IMMEDIATE) {
                        1
                    } else {
                        -1isize as usize
                    };
                    return Ok(Some((xt, flag)));
                }
            }
            xt = self.data.read_cell(header.link_addr())?;
        }
        Ok(None)
    }

    pub(crate) fn set_diagnostic(&mut self, addr: usize, len: usize) -> Result<()> {
        self.data
            .write_cell(self.layout_addr(Layout::DIAGNOSTIC_ADDR), addr)?;
        self.data
            .write_cell(self.layout_addr(Layout::DIAGNOSTIC_LEN), len)?;
        Ok(())
    }

    pub(super) fn set_source(&mut self, code: &[u8]) -> Result<()> {
        if code.len() > INPUT_BUFFER_SIZE {
            return Err(Error::Throw(Ior::PARSED_STRING_OVERFLOW));
        }
        let input_addr = self.layout_addr(Layout::INPUT);
        self.data.write(input_addr, code)?;
        self.set_source_len(code.len())
    }

    pub(crate) fn set_source_len(&mut self, len: usize) -> Result<()> {
        let input_addr = self.layout_addr(Layout::INPUT);
        self.data
            .write_cell(self.layout_addr(Layout::SOURCE_ADDR), input_addr)?;
        self.data
            .write_cell(self.layout_addr(Layout::SOURCE_LEN), len)?;
        self.data
            .write_cell(self.layout_addr(Layout::SOURCE_ID), -1isize as usize)?;
        Ok(self.data.write_cell(self.layout_addr(Layout::TO_IN), 0)?)
    }

    fn layout_addr(&self, offset: usize) -> usize {
        self.base_addr + offset
    }
}