aboutsummaryrefslogtreecommitdiffstats
path: root/libalee
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2023-03-08 19:57:26 -0500
committerClyne Sullivan <clyne@bitgloo.com>2023-03-08 19:57:26 -0500
commitdcd5e792b1d84afd1bea9780781674b6e6ad8dc3 (patch)
tree6b841cd741c3d8df6cb7b22f1423a5c814e0add9 /libalee
parent1960aa8773e51a76f0a5a653c8a4eb4fbef5e4ba (diff)
move libalee into folder
Diffstat (limited to 'libalee')
-rw-r--r--libalee/corewords.cpp253
-rw-r--r--libalee/corewords.hpp52
-rw-r--r--libalee/dictionary.cpp176
-rw-r--r--libalee/dictionary.hpp79
-rw-r--r--libalee/parser.cpp112
-rw-r--r--libalee/parser.hpp40
-rw-r--r--libalee/state.cpp65
-rw-r--r--libalee/state.hpp109
-rw-r--r--libalee/types.hpp44
9 files changed, 930 insertions, 0 deletions
diff --git a/libalee/corewords.cpp b/libalee/corewords.cpp
new file mode 100644
index 0000000..4de9413
--- /dev/null
+++ b/libalee/corewords.cpp
@@ -0,0 +1,253 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "corewords.hpp"
+
+#include <cstring>
+#include <utility>
+
+Word getword(State& state)
+{
+ auto word = state.dict.input();
+ while (word.size() == 0) {
+ state.input(state);
+ word = state.dict.input();
+ }
+ return word;
+}
+void newdef(State& state, Word word)
+{
+ auto& dict = state.dict;
+
+ auto addr = dict.alignhere();
+ dict.addDefinition(word);
+ state.push(addr);
+};
+void tick(State& state)
+{
+ auto word = getword(state);
+ if (auto j = state.dict.find(word); j > 0) {
+ state.push(state.dict.getexec(j));
+ auto imm = state.dict.read(j) & CoreWords::Immediate;
+ state.push(imm ? 1 : -1);
+ } else if (auto i = CoreWords::findi(state, word); i >= 0) {
+ state.push(i);
+ state.push(i == CoreWords::Semicolon ? 1 : -1);
+ } else {
+ state.push(0);
+ state.push(0);
+ }
+}
+
+void CoreWords::run(unsigned int index, State& state)
+{
+ Cell cell;
+ DoubleCell dcell;
+
+execute:
+ if (/*(index & 1) == 0 &&*/ index >= WordCount) {
+ // must be calling a defined subroutine
+ state.pushr(state.ip);
+ state.ip = index;
+ return;
+ } else switch (index & 0x1F) {
+ case 0: // _lit
+ state.push(/*(index & 0xFF00) ? ((Addr)index >> 8u) - 1 :*/ state.beyondip());
+ break;
+ case 1: // drop
+ state.pop();
+ break;
+ case 2: // dup
+ state.push(state.top());
+ break;
+ case 3: // swap
+ std::swap(state.top(), state.pick(1));
+ break;
+ case 4: // pick
+ state.push(state.pick(state.pop()));
+ break;
+ case 5: // sys
+ user_sys(state);
+ break;
+ case 6: // add
+ cell = state.pop();
+ state.top() += cell;
+ break;
+ case 7: // sub
+ cell = state.pop();
+ state.top() -= cell;
+ break;
+ case 8: // mul ( n n -- d )
+ cell = state.pop();
+ dcell = state.pop() * cell;
+ state.push(dcell);
+ state.push(dcell >> (sizeof(Cell) * 8));
+ break;
+ case 9: // div ( d n -- n )
+ cell = state.pop();
+ dcell = state.pop();
+ dcell <<= sizeof(Cell) * 8;
+ dcell |= static_cast<Addr>(state.pop());
+ state.push(dcell / cell);
+ break;
+ case 10: // mod ( d n -- n )
+ cell = state.pop();
+ dcell = state.pop();
+ dcell <<= sizeof(Cell) * 8;
+ dcell |= static_cast<Addr>(state.pop());
+ state.push(dcell % cell);
+ break;
+ case 11: // peek
+ if (state.pop())
+ state.push(state.dict.read(state.pop()));
+ else
+ state.push(state.dict.readbyte(state.pop()));
+ break;
+ case 12: // poke
+ cell = state.pop();
+ if (auto addr = state.pop(); cell)
+ state.dict.write(addr, state.pop());
+ else
+ state.dict.writebyte(addr, state.pop());
+ break;
+ case 13: // pushr
+ state.pushr(state.pop());
+ break;
+ case 14: // popr
+ state.push(state.popr());
+ break;
+ case 15: // equal
+ cell = state.pop();
+ state.top() = state.top() == cell ? -1 : 0;
+ break;
+ case 16: // lt
+ cell = state.pop();
+ state.top() = state.top() < cell ? -1 : 0;
+ break;
+ case 17: // and
+ cell = state.pop();
+ state.top() &= cell;
+ break;
+ case 18: // or
+ cell = state.pop();
+ state.top() |= cell;
+ break;
+ case 19: // xor
+ cell = state.pop();
+ state.top() ^= cell;
+ break;
+ case 20: // shl
+ cell = state.pop();
+ reinterpret_cast<Addr&>(state.top()) <<= static_cast<Addr>(cell);
+ break;
+ case 21: // shr
+ cell = state.pop();
+ reinterpret_cast<Addr&>(state.top()) >>= static_cast<Addr>(cell);
+ break;
+ case 22: // colon
+ newdef(state, getword(state));
+ state.compiling(true);
+ break;
+ case 23: // tick
+ tick(state);
+ break;
+ case 24: // execute
+ index = state.pop();
+ goto execute;
+ case 25: // exit
+ state.ip = state.popr();
+ if (state.ip == 0) {
+ std::longjmp(state.jmpbuf,
+ static_cast<int>(State::Error::exit));
+ }
+ break;
+ case 26: // semic
+ {
+ state.dict.add(findi("exit"));
+ state.compiling(false);
+
+ auto addr = state.pop();
+ state.dict.write(addr,
+ (state.dict.read(addr) & 0x1F) |
+ ((addr - state.dict.latest()) << 6));
+ state.dict.latest(addr);
+ }
+ break;
+ case 27: // _jmp0
+ if (state.pop()) {
+ state.beyondip();
+ break;
+ }
+ [[fallthrough]];
+ case 28: // _jmp
+ state.ip = state.beyondip();
+ return;
+ case 29: // depth
+ state.push(state.size());
+ break;
+ case 30: // _rdepth
+ state.push(state.rsize());
+ break;
+ case 31: // _in
+ state.input(state);
+ break;
+ }
+
+ state.ip += sizeof(Cell);
+}
+
+int CoreWords::findi(const char *word)
+{
+ std::size_t i = 0;
+ int wordsi = 0;
+
+ while (i < sizeof(wordsarr)) {
+ auto end = i;
+ while (wordsarr[end])
+ ++end;
+
+ if (!std::strcmp(word, wordsarr + i))
+ return wordsi;
+
+ ++wordsi;
+ i = end + 1;
+ }
+
+ return -1;
+}
+
+int CoreWords::findi(State& state, Word word)
+{
+ std::size_t i = 0;
+ int wordsi = 0;
+
+ while (i < sizeof(wordsarr)) {
+ auto end = i;
+ while (wordsarr[end])
+ ++end;
+
+ if (state.dict.equal(word, wordsarr + i, end - i))
+ return wordsi;
+
+ ++wordsi;
+ i = end + 1;
+ }
+
+ return -1;
+}
+
diff --git a/libalee/corewords.hpp b/libalee/corewords.hpp
new file mode 100644
index 0000000..21a0951
--- /dev/null
+++ b/libalee/corewords.hpp
@@ -0,0 +1,52 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALEEFORTH_COREWORDS_HPP
+#define ALEEFORTH_COREWORDS_HPP
+
+#include "types.hpp"
+#include "state.hpp"
+
+void user_sys(State&);
+
+class CoreWords
+{
+public:
+ constexpr static std::size_t WordCount = 32;
+
+ constexpr static Cell Immediate = (1 << 5);
+
+ constexpr static int Semicolon = 26;
+
+ static int findi(const char *);
+ static int findi(State&, Word);
+ static void run(unsigned int, State&);
+
+private:
+ constexpr static char wordsarr[] =
+ "_lit\0drop\0dup\0swap\0pick\0sys\0"
+ "+\0-\0m*\0_/\0_%\0"
+ "_@\0_!\0>r\0r>\0=\0"
+ "<\0&\0|\0^\0"
+ "<<\0>>\0:\0_'\0execute\0"
+ "exit\0;\0_jmp0\0_jmp\0"
+ "depth\0_rdepth\0_in\0";
+};
+
+#endif // ALEEFORTH_COREWORDS_HPP
+
diff --git a/libalee/dictionary.cpp b/libalee/dictionary.cpp
new file mode 100644
index 0000000..48230c4
--- /dev/null
+++ b/libalee/dictionary.cpp
@@ -0,0 +1,176 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "dictionary.hpp"
+
+#include <cctype>
+#include <cstring>
+
+void Dictionary::initialize()
+{
+ write(Base, 10);
+ write(Here, Begin);
+ write(Latest, Begin);
+ write(Compiling, 0);
+ write(Source, Input + sizeof(Cell));
+}
+
+Addr Dictionary::allot(Cell amount) noexcept
+{
+ Addr old = here();
+ here(old + amount);
+ return old;
+}
+
+void Dictionary::add(Cell value) noexcept
+{
+ write(allot(sizeof(Cell)), value);
+}
+
+Addr Dictionary::aligned(Addr addr) const noexcept
+{
+ auto unaligned = addr & (sizeof(Cell) - sizeof(uint8_t));
+ if (unaligned)
+ addr += sizeof(Cell) - unaligned;
+
+ return addr;
+}
+
+Addr Dictionary::alignhere() noexcept
+{
+ here(aligned(here()));
+ return here();
+}
+
+void Dictionary::addDefinition(Word word) noexcept
+{
+ add(word.size());
+ for (auto w = word.start; w != word.end; ++w)
+ writebyte(allot(1), readbyte(w));
+
+ alignhere();
+}
+
+Addr Dictionary::find(Word word) noexcept
+{
+ Addr lt = latest(), oldlt;
+ do {
+ oldlt = lt;
+ const auto l = static_cast<Addr>(read(lt));
+ const Addr len = l & 0x1F;
+ const Word lw {
+ static_cast<Addr>(lt + sizeof(Cell)),
+ static_cast<Addr>(lt + sizeof(Cell) + len)
+ };
+
+ if (equal(word, lw))
+ return lt;
+ else
+ lt -= l >> 6;
+ } while (lt != oldlt);
+
+ return 0;
+}
+
+Addr Dictionary::getexec(Addr addr) noexcept
+{
+ const auto len = read(addr) & 0x1F;
+ return aligned(addr + sizeof(Cell) + len);
+}
+
+Word Dictionary::input() noexcept
+{
+ auto src = read(Dictionary::Source);
+ auto end = read(Dictionary::SourceLen);
+ auto idx = read(Dictionary::Input);
+
+ Addr wordstart = src + idx;
+ Addr wordend = wordstart;
+
+ while (idx < end) {
+ auto ch = readbyte(wordend);
+
+ if (ch == '\0')
+ break;
+
+ if (isspace(ch)) {
+ if (wordstart != wordend) {
+ writebyte(Dictionary::Input, idx + 1);
+ return {wordstart, wordend};
+ }
+
+ ++wordstart;
+ }
+
+ ++wordend;
+ ++idx;
+ }
+
+ if (wordstart != wordend) {
+ writebyte(Dictionary::Input, idx + 1);
+ return {wordstart, wordend};
+ }
+
+ return {};
+}
+
+bool Dictionary::equal(Word word, const char *str, unsigned len) const noexcept
+{
+ if (word.size() != len)
+ return false;
+
+ for (auto w = word.start; w != word.end; ++w) {
+ auto wc = readbyte(w);
+ if (wc != *str) {
+ if (isalpha(wc) && isalpha(*str) && (wc | 32) == (*str | 32)) {
+ ++str;
+ continue;
+ }
+
+ return false;
+ }
+
+ ++str;
+ }
+
+ return true;
+}
+
+bool Dictionary::equal(Word word, Word other) const noexcept
+{
+ if (word.size() != other.size())
+ return false;
+
+ auto w = word.start, o = other.start;
+ while (w != word.end) {
+ auto wc = readbyte(w), oc = readbyte(o);
+ if (wc != oc) {
+ if (isalpha(wc) && isalpha(oc) && (wc | 32) == (oc | 32)) {
+ ++w, ++o;
+ continue;
+ }
+
+ return false;
+ }
+
+ ++w, ++o;
+ }
+
+ return true;
+}
+
diff --git a/libalee/dictionary.hpp b/libalee/dictionary.hpp
new file mode 100644
index 0000000..4dcae77
--- /dev/null
+++ b/libalee/dictionary.hpp
@@ -0,0 +1,79 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALEEFORTH_DICTIONARY_HPP
+#define ALEEFORTH_DICTIONARY_HPP
+
+#include "types.hpp"
+
+#include <cstddef>
+#include <cstdint>
+
+/**
+ * Dictionary entry format:
+ * - 1 information byte
+ * bits 0..4: Length of name (L)
+ * bit 5: Immediate?
+ * bits 6..15: Distance to next entry (negative)
+ * - L bytes of name
+ * - 0+ bytes for address alignment
+ * - 0+ bytes of entry's data...
+ */
+
+class Dictionary
+{
+public:
+ constexpr static Addr Base = 0;
+ constexpr static Addr Here = sizeof(Cell);
+ constexpr static Addr Latest = sizeof(Cell) * 2;
+ constexpr static Addr Compiling = sizeof(Cell) * 3;
+ constexpr static Addr Source = sizeof(Cell) * 4;
+ constexpr static Addr SourceLen = sizeof(Cell) * 5;
+ constexpr static Addr Input = sizeof(Cell) * 6; // len data...
+ constexpr static Addr InputCells = 80; // bytes!
+ constexpr static Addr Begin = sizeof(Cell) * 7 + InputCells;
+
+ void initialize();
+
+ Addr here() const noexcept { return read(Here); }
+ void here(Addr l) noexcept { write(Here, l); }
+
+ Addr latest() const noexcept { return read(Latest); }
+ void latest(Addr l) noexcept { write(Latest, l); }
+
+ virtual Cell read(Addr) const noexcept = 0;
+ virtual void write(Addr, Cell) noexcept = 0;
+ virtual uint8_t readbyte(Addr) const noexcept = 0;
+ virtual void writebyte(Addr, uint8_t) noexcept = 0;
+
+ Addr alignhere() noexcept;
+ Addr aligned(Addr) const noexcept;
+ Addr allot(Cell) noexcept;
+ void add(Cell) noexcept;
+ void addDefinition(Word) noexcept;
+
+ Addr find(Word) noexcept;
+ Addr getexec(Addr) noexcept;
+ Word input() noexcept;
+
+ bool equal(Word, const char *, unsigned) const noexcept;
+ bool equal(Word, Word) const noexcept;
+};
+
+#endif // ALEEFORTH_DICTIONARY_HPP
+
diff --git a/libalee/parser.cpp b/libalee/parser.cpp
new file mode 100644
index 0000000..44b5cec
--- /dev/null
+++ b/libalee/parser.cpp
@@ -0,0 +1,112 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "corewords.hpp"
+#include "parser.hpp"
+
+#include <charconv>
+#include <cstring>
+
+int Parser::parse(State& state, const char *str)
+{
+ auto addr = Dictionary::Input;
+ state.dict.write(addr, 0);
+ state.dict.write(Dictionary::SourceLen, std::strlen(str));
+
+ addr += sizeof(Cell);
+ while (*str)
+ state.dict.writebyte(addr++, *str++);
+
+ while (addr < Dictionary::Input + Dictionary::InputCells)
+ state.dict.writebyte(addr++, '\0');
+
+ return parseSource(state);
+}
+
+int Parser::parseSource(State& state)
+{
+ auto word = state.dict.input();
+ while (word.size() > 0) {
+ if (auto ret = parseWord(state, word); ret)
+ return ret;
+
+ word = state.dict.input();
+ }
+
+ return 0;
+}
+
+int Parser::parseWord(State& state, Word word)
+{
+ int ins, imm;
+
+ ins = state.dict.find(word);
+
+ if (ins <= 0) {
+ ins = CoreWords::findi(state, word);
+
+ if (ins < 0)
+ return parseNumber(state, word);
+ else
+ imm = ins == CoreWords::Semicolon;
+ } else {
+ imm = state.dict.read(ins) & CoreWords::Immediate;
+ ins = state.dict.getexec(ins);
+ }
+
+ if (state.compiling() && !imm)
+ state.dict.add(ins);
+ else if (auto stat = state.execute(ins); stat != State::Error::none)
+ return static_cast<int>(stat);
+
+ return 0;
+}
+
+int Parser::parseNumber(State& state, Word word)
+{
+ char buf[MaxCellNumberChars + 1];
+ unsigned i;
+ for (i = 0; i < std::min(MaxCellNumberChars, word.size()); ++i)
+ buf[i] = state.dict.readbyte(word.start + i);
+ buf[i] = '\0';
+
+ auto base = state.dict.read(0);
+ DoubleCell dl;
+ auto [ptr, ec] = std::from_chars(buf, buf + i, dl, base);
+ Cell l = static_cast<Cell>(dl);
+
+ if (ec == std::errc() && ptr == buf + i) {
+ if (state.compiling()) {
+ auto ins = CoreWords::findi("_lit");
+
+ //if (l >= 0 && l < 0xFF) {
+ // state.dict.add(ins | ((l + 1) << 8));
+ //} else {
+ state.dict.add(ins);
+ state.dict.add(l);
+ //}
+ } else {
+ state.push(l);
+ }
+
+ return 0;
+ } else {
+ return UnknownWord;
+ }
+}
+
diff --git a/libalee/parser.hpp b/libalee/parser.hpp
new file mode 100644
index 0000000..4b14d64
--- /dev/null
+++ b/libalee/parser.hpp
@@ -0,0 +1,40 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALEEFORTH_PARSER_HPP
+#define ALEEFORTH_PARSER_HPP
+
+#include "types.hpp"
+
+#include <string_view>
+
+class Parser
+{
+public:
+ constexpr static int UnknownWord = -1;
+
+ int parse(State&, const char *);
+ int parseSource(State&);
+
+private:
+ int parseWord(State&, Word);
+ int parseNumber(State&, Word);
+};
+
+#endif // ALEEFORTH_PARSER_HPP
+
diff --git a/libalee/state.cpp b/libalee/state.cpp
new file mode 100644
index 0000000..ea6c601
--- /dev/null
+++ b/libalee/state.cpp
@@ -0,0 +1,65 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "corewords.hpp"
+#include "state.hpp"
+
+#include <iterator>
+
+bool State::compiling() const
+{
+ return dict.read(Dictionary::Compiling);
+}
+
+void State::compiling(bool yes)
+{
+ dict.write(Dictionary::Compiling, yes);
+}
+
+State::Error State::execute(Addr addr)
+{
+ auto stat = static_cast<State::Error>(setjmp(jmpbuf));
+
+ if (stat == State::Error::none) {
+ CoreWords::run(addr, *this);
+
+ if (ip >= Dictionary::Begin) {
+ // longjmp will exit this loop.
+ for (;;)
+ CoreWords::run(dict.read(ip), *this);
+ } else {
+ // addr was a CoreWord, all done now.
+ ip = 0;
+ }
+ } else if (stat == State::Error::exit) {
+ stat = State::Error::none;
+ }
+
+ return stat;
+}
+
+std::size_t State::size() const noexcept
+{
+ return std::distance(dstack, static_cast<const Cell *>(dsp)) + 1;
+}
+
+std::size_t State::rsize() const noexcept
+{
+ return std::distance(rstack, static_cast<const Cell *>(rsp)) + 1;
+}
+
diff --git a/libalee/state.hpp b/libalee/state.hpp
new file mode 100644
index 0000000..28396dc
--- /dev/null
+++ b/libalee/state.hpp
@@ -0,0 +1,109 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALEEFORTH_STATE_HPP
+#define ALEEFORTH_STATE_HPP
+
+#include "dictionary.hpp"
+#include "types.hpp"
+
+#include <csetjmp>
+#include <cstddef>
+
+constexpr unsigned DataStackSize = 16;
+constexpr unsigned ReturnStackSize = 16;
+
+struct State
+{
+ enum class Error : int {
+ none = 0,
+ push,
+ pop,
+ pushr,
+ popr,
+ top,
+ pick,
+ exit
+ };
+
+ Addr ip = 0;
+ Dictionary& dict;
+ void (*input)(State&);
+
+ Cell dstack[DataStackSize] = {};
+ Cell rstack[ReturnStackSize] = {};
+ Cell *dsp = dstack - 1;
+ Cell *rsp = rstack - 1;
+
+ std::jmp_buf jmpbuf = {};
+
+ constexpr State(Dictionary& d, void (*i)(State&)):
+ dict(d), input(i) {}
+
+ bool compiling() const;
+ void compiling(bool);
+
+ Error execute(Addr);
+
+ std::size_t size() const noexcept;
+ std::size_t rsize() const noexcept;
+
+ inline void push(Cell value) {
+ if (dsp == dstack + DataStackSize - 1)
+ std::longjmp(jmpbuf, static_cast<int>(Error::push));
+ *++dsp = value;
+ }
+
+ inline Cell pop() {
+ if (dsp < dstack)
+ std::longjmp(jmpbuf, static_cast<int>(Error::pop));
+ return *dsp--;
+ }
+
+ inline Cell beyondip() {
+ ip += sizeof(Cell);
+ return dict.read(ip);
+ }
+
+ inline void pushr(Cell value) {
+ if (rsp == rstack + ReturnStackSize - 1)
+ std::longjmp(jmpbuf, static_cast<int>(Error::pushr));
+ *++rsp = value;
+ }
+
+ inline Cell popr() {
+ if (rsp < rstack)
+ std::longjmp(jmpbuf, static_cast<int>(Error::popr));
+ return *rsp--;
+ }
+
+ inline Cell& top() {
+ if (dsp < dstack)
+ std::longjmp(jmpbuf, static_cast<int>(Error::top));
+ return *dsp;
+ }
+
+ inline Cell& pick(std::size_t i) {
+ if (dsp - i < dstack)
+ std::longjmp(jmpbuf, static_cast<int>(Error::pick));
+ return *(dsp - i);
+ }
+};
+
+#endif // ALEEFORTH_STATE_HPP
+
diff --git a/libalee/types.hpp b/libalee/types.hpp
new file mode 100644
index 0000000..530ff4f
--- /dev/null
+++ b/libalee/types.hpp
@@ -0,0 +1,44 @@
+/**
+ * Alee Forth: A portable and concise Forth implementation in modern C++.
+ * Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALEEFORTH_TYPES_HPP
+#define ALEEFORTH_TYPES_HPP
+
+#include <cstdint>
+
+struct State;
+
+using Addr = uint16_t;
+using Cell = int16_t;
+using DoubleCell = int32_t;
+using Func = void (*)(State&);
+
+constexpr unsigned int MaxCellNumberChars = 6; // -32768
+
+struct Word
+{
+ Addr start = 0;
+ Addr end = 0;
+
+ unsigned size() const noexcept {
+ return end - start;
+ }
+};
+
+#endif // ALEEFORTH_TYPES_HPP
+