aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2024-11-30 10:02:26 -0500
committerClyne Sullivan <clyne@bitgloo.com>2024-11-30 10:02:26 -0500
commitcb04555442286affb56a95a54c428f8f643b3503 (patch)
tree7e3a2347745bdb052deaff2d136f7b2a14ea1321
parentd2cff5f967bb1e625ad54d400059965a04618c4a (diff)
create namespace; refactor header
-rw-r--r--forth.hpp513
-rw-r--r--main.cpp18
-rw-r--r--sforth/comp_word.hpp92
-rw-r--r--sforth/forth.hpp345
-rw-r--r--sforth/native_word.hpp64
-rw-r--r--sforth/types.hpp106
6 files changed, 616 insertions, 522 deletions
diff --git a/forth.hpp b/forth.hpp
deleted file mode 100644
index 7357508..0000000
--- a/forth.hpp
+++ /dev/null
@@ -1,513 +0,0 @@
-/// sforth, an implementation of forth
-/// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
-///
-/// This program is free software: you can redistribute it and/or modify it
-/// under the terms of the GNU 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 General Public License for
-/// more details.
-///
-/// You should have received a copy of the GNU General Public License along
-/// with this program. If not, see <http://www.gnu.org/licenses/>.
-
-#ifndef SFORTH_HPP
-#define SFORTH_HPP
-
-#include <algorithm>
-#include <array>
-#include <bit>
-#include <charconv>
-#include <cstdint>
-#include <cstddef>
-#include <iterator>
-#include <span>
-#include <string_view>
-#include <tuple>
-#include <utility>
-
-using cell = std::intptr_t;
-using addr = std::uintptr_t;
-using func = void (*)(const void *);
-
-struct word_base;
-
-struct word_list
-{
- const word_base *next;
-
- constexpr word_list(const word_base *n = nullptr): next{n} {}
-
- std::optional<const word_base *> get(std::string_view sv) const;
-
- static constexpr auto parse(const char *source, std::size_t& sourcei) -> std::string_view {
- const std::string_view sv {source};
-
- const auto e = sv.find_first_of(" \t\r\n", sourcei);
- const auto word = e != std::string_view::npos ?
- sv.substr(sourcei, e - sourcei) : sv.substr(sourcei);
-
- sourcei = sv.find_first_not_of(" \t\r\n", e);
- return word;
- }
-};
-
-struct word_base : public word_list
-{
- static constexpr addr immediate = 1 << 8;
-
- addr flags_len;
-
- constexpr word_base(const word_base *n, addr fl):
- word_list{n}, flags_len{fl} {}
-
- std::string_view name() const {
- return {std::bit_cast<const char *>(this + 1)};
- }
-
- const func *body() const {
- const auto ptr = std::bit_cast<const std::uint8_t *>(this + 1);
- const auto fptr = ptr + (flags_len & 0xFF);
- return std::bit_cast<const func *>(fptr);
- }
-
- constexpr void make_immediate() {
- flags_len |= immediate;
- }
-};
-
-template<unsigned N>
-struct cS {
- char data[N];
-
- consteval cS(const char (&s)[N]) {
- std::copy(s, s + N, data);
- }
- consteval operator const char *() const {
- return data;
- }
- consteval auto size() const {
- return N;
- }
-};
-
-struct forth : public word_list
-{
- static constexpr bool enable_exceptions = true;
- static constexpr int data_size = 16;
- static constexpr int return_size = 16;
-
- static constexpr auto npos = std::string_view::npos;
-
- enum class error {
- init_error,
- parse_error,
- execute_error,
- dictionary_overflow,
- word_not_found,
- stack_underflow,
- stack_overflow,
- return_stack_underflow,
- return_stack_overflow,
- compile_only_word
- };
-
- template<error Err>
- static inline void assert(bool condition) {
- if constexpr (enable_exceptions) {
- if (!condition)
- throw Err;
- }
- }
-
- void push(cell v) {
- assert<error::stack_overflow>(sp != dstack.begin());
- *--sp = v;
- }
-
- void push(cell v, auto... vs) {
- push(v); (push(vs), ...);
- }
-
- void rpush(func *v) {
- assert<error::return_stack_overflow>(rp != rstack.begin());
- *--rp = v;
- }
-
- cell& top() {
- assert<error::stack_underflow>(sp != dstack.end());
- return *sp;
- }
-
- cell pop() {
- assert<error::stack_underflow>(sp != dstack.end());
- return *sp++;
- }
-
- auto rpop() -> func * {
- assert<error::return_stack_underflow>(rp != rstack.end());
- return *rp++;
- }
-
- template<int N>
- auto pop() {
- static_assert(N > 0, "pop<N>() with N <= 0");
-
- auto t = std::tuple {pop()};
- if constexpr (N > 1)
- return std::tuple_cat(t, pop<N - 1>());
- else
- return t;
- }
-
- forth& add(std::string_view name, func entry = nullptr) {
- const auto namesz = (name.size() + 1 + sizeof(cell) - 1) & ~(sizeof(cell) - 1);
- const auto size = (sizeof(word_base) + namesz) / sizeof(cell);
-
- assert<error::parse_error>(!name.empty());
- //assert<error::dictionary_overflow>(state->here + size < &dictionary.back());
-
- const auto h = std::exchange(here, here + size);
- next = new (h) word_base (next, namesz);
- std::copy(name.begin(), name.end(),
- std::bit_cast<char *>(h) + sizeof(word_base));
- if (entry)
- *here++ = std::bit_cast<cell>(entry);
- return *this;
- }
-
- auto parse() -> std::string_view {
- return word_list::parse(source, sourcei);
- }
-
- void parse_line(std::string_view sv) {
- source = sv.data();
- sourcei = sv.find_first_not_of(" \t\r\n");
-
- while (sourcei != npos) {
- const auto word = parse();
-
- if (auto ent = get(word); !ent) {
- cell n;
- const auto [p, e] = std::from_chars(word.cbegin(), word.cend(),
- n, base);
-
- assert<error::word_not_found>(e == std::errc() && p == word.cend());
-
- push(n);
-
- if (compiling)
- execute((*get("literal"))->body());
- } else {
- auto body = (*ent)->body();
-
- if (compiling && ((*ent)->flags_len & word_base::immediate) == 0) {
- *here++ = std::bit_cast<cell>(body);
- } else {
- execute(body);
- }
- }
- }
- }
-
- void execute(const func *body) {
- assert<error::execute_error>(body && *body);
- (*body)(body);
- }
-
- template<forth **fthp>
- static void prologue(const void *bodyf) {
- static auto& fth = **fthp;
-
- auto body = (func *)bodyf;
- fth.rpush(fth.ip);
-
- for (fth.ip = body + 1; *fth.ip; fth.ip++)
- fth.execute(std::bit_cast<func *>(*fth.ip));
-
- fth.ip = fth.rpop();
- }
-
- template<forth** fthp>
- static void initialize(cell *end_value);
-
- static auto error_string(error err) noexcept -> std::string_view {
- using enum error;
- switch (err) {
- case init_error: return "init error";
- case parse_error: return "parse error";
- case execute_error: return "execute error";
- case dictionary_overflow: return "dictionary overflow";
- case word_not_found: return "word not found";
- case stack_underflow: return "stack underflow";
- case stack_overflow: return "stack overflow";
- case return_stack_underflow: return "return stack underflow";
- case return_stack_overflow: return "return stack overflow";
- case compile_only_word: return "compile only word";
- default: return "unknown error";
- }
- }
-
- constexpr forth() {
- sp = dstack.end();
- rp = rstack.end();
- }
-
- cell *sp;
- func **rp;
- func *ip = nullptr;
- cell *here = std::bit_cast<cell *>(this + 1);
- const char *source = nullptr;
- std::size_t sourcei = npos;
- cell compiling = false;
- cell *end = nullptr;
- cell base = 10;
- std::array<cell, data_size> dstack;
- std::array<func *, return_size> rstack;
-};
-
-template<cS Name, func Body, auto *Prev = (const word_base *)nullptr>
-struct native_word : public word_base
-{
- constexpr static auto N = (sizeof(Name) + sizeof(cell) - 1) & ~(sizeof(cell) - 1);
- std::array<char, N> namebuf;
- func body;
-
- consteval const func *get_ct(std::string_view name) const {
- if (name == std::string_view{Name.data})
- return &body;
- else if constexpr (Prev != nullptr)
- return Prev->get_ct(name);
- else
- return nullptr;
- }
-
- consteval native_word(addr flags = 0):
- word_base{Prev, N | flags}, namebuf{}, body{Body}
- {
- std::copy(Name.data, Name.data + sizeof(Name), namebuf.data());
- }
-};
-
-template<const func Prol, cS Name, cS Body, auto *Prev = (const word_base *)nullptr>
-struct comp_word : public native_word<Name, Prol, Prev>
-{
- static constexpr std::size_t B =
- [] {
- std::size_t b = 1;
- std::string_view sv {Body.data};
- auto sourcei = sv.find_first_not_of(" \t\r\n");
- while (sourcei != std::string_view::npos) {
- const auto word = word_list::parse(Body.data, sourcei);
-
- b++;
- if (!Prev->get_ct(word))
- b++;
- }
- return b;
- }();
-
- union bodyt {
- const func *f;
- cell c;
- };
-
- std::array<bodyt, B> bodybuf {};
-
- consteval comp_word(addr flags = 0):
- native_word<Name, Prol, Prev>{flags}
- {
- auto bptr = bodybuf.begin();
- std::string_view sv {Body};
- auto sourcei = sv.find_first_not_of(" \t\r\n");
- while (sourcei != std::string_view::npos) {
- const auto word = word_list::parse(Body, sourcei);
-
- auto w = Prev->get_ct(word);
- if (w) {
- bptr->f = Prev->get_ct(word);
- bptr++;
- } else {
- cell n;
- std::from_chars(word.cbegin(), word.cend(), n, 10);
-
- bptr->f = Prev->get_ct("_lit");
- bptr++;
- bptr->c = n;
- bptr++;
- }
- }
- }
-};
-
-template<cS Name, func Body, addr Flags, auto... Next>
-struct native_dict
-{
- constexpr static native_word<Name, Body,
- [] {
- if constexpr (sizeof...(Next))
- return &native_dict<Next...>::word;
- else
- return (const word_base *)nullptr;
- }()> word {Flags};
-};
-
-template<func Prol, auto *Prev, cS Name, cS Body, addr Flags, auto... Next>
-struct comp_dict
-{
- constexpr static comp_word<Prol, Name, Body,
- [] {
- if constexpr (sizeof...(Next))
- return &comp_dict<Prol, Prev, Next...>::word;
- else
- return Prev;
- }()> word {Flags};
-};
-
-std::optional<const word_base *> word_list::get(std::string_view sv) const
-{
- for (auto lt = next; lt; lt = lt->next) {
- if (sv == lt->name())
- return lt;
- }
-
- return {};
-}
-
-template<forth** fthp>
-void forth::initialize(cell *end_value)
-{
- assert<error::init_error>(*fthp);
-
- static auto& fth = **fthp;
- static auto _d = std::bit_cast<cell>(*fthp);
-
- constexpr static func lit_impl = [](auto) {
- auto ptr = std::bit_cast<cell *>(++fth.ip);
- fth.push(*ptr);
- };
-
- constexpr static auto& dict1 = native_dict<
- cS{"_d" }, [](auto) { fth.push(_d); }, 0
- , cS{"sp" }, [](auto) { fth.push(_d + sizeof(cell)); }, 0
- , cS{"rp" }, [](auto) { fth.push(_d + 2 * sizeof(cell)); }, 0
- , cS{"ip" }, [](auto) { fth.push(_d + 3 * sizeof(cell)); }, 0
- , cS{"dp" }, [](auto) { fth.push(_d + 4 * sizeof(cell)); }, 0
- , cS{"state"}, [](auto) { fth.push(_d + 7 * sizeof(cell)); }, 0
- , cS{"base" }, [](auto) { fth.push(_d + 9 * sizeof(cell)); }, 0
- , cS{"_lit" }, lit_impl, 0
- , cS{"swap" }, [](auto) { auto [a, b] = fth.pop<2>(); fth.push(a, b); }, 0
- , cS{"drop" }, [](auto) { fth.pop(); }, 0
- , cS{"dup" }, [](auto) { fth.push(fth.top()); }, 0
- , cS{"rot" }, [](auto) { auto [a, b, c] = fth.pop<3>(); fth.push(b, a, c); }, 0
- , cS{"+" }, [](auto) { fth.top() += fth.pop(); }, 0
- , cS{"-" }, [](auto) { fth.top() -= fth.pop(); }, 0
- , cS{"*" }, [](auto) { fth.top() *= fth.pop(); }, 0
- , cS{"/" }, [](auto) { fth.top() /= fth.pop(); }, 0
- , cS{"mod" }, [](auto) { fth.top() %= fth.pop(); }, 0
- , cS{"and" }, [](auto) { fth.top() &= fth.pop(); }, 0
- , cS{"or" }, [](auto) { fth.top() |= fth.pop(); }, 0
- , cS{"xor" }, [](auto) { fth.top() ^= fth.pop(); }, 0
- , cS{"lshift"}, [](auto) { fth.top() <<= fth.pop(); }, 0
- , cS{"rshift"}, [](auto) { fth.top() >>= fth.pop(); }, 0
- , cS{"[" }, [](auto) { fth.compiling = false; }, word_base::immediate
- , cS{"]" }, [](auto) { fth.compiling = true; }, 0
- , cS{"immediate"}, [](auto) { const_cast<word_base *>(fth.next)->make_immediate(); }, 0
- , cS{"literal"}, [](auto) {
- //assert<error::compile_only_word>(fth.compiling);
- *fth.here++ = std::bit_cast<cell>(&lit_impl);
- *fth.here++ = fth.pop(); }, word_base::immediate
- , cS{"@" }, [](auto) { fth.push(*std::bit_cast<cell *>(fth.pop())); }, 0
- , cS{"!" }, [](auto) { auto [p, v] = fth.pop<2>(); *std::bit_cast<cell *>(p) = v; }, 0
- , cS{"c@" }, [](auto) { fth.push(*std::bit_cast<char *>(fth.pop())); }, 0
- , cS{"c!" }, [](auto) { auto [p, v] = fth.pop<2>(); *std::bit_cast<char *>(p) = v; }, 0
- , cS{"=" }, [](auto) { auto v = fth.pop(); fth.top() = -(fth.top() == v); }, 0
- , cS{"<" }, [](auto) { auto v = fth.pop(); fth.top() = -(fth.top() < v); }, 0
- , cS{"\'" }, [](auto) {
- auto w = fth.parse();
- auto g = fth.get(w);
- fth.push(g ? std::bit_cast<cell>((*g)->body()) : 0); }, 0
- , cS{":" }, [](auto) {
- const auto prologue = forth::prologue<fthp>;
- auto w = fth.parse();
- fth.add(w);
- *fth.here++ = std::bit_cast<cell>(prologue);
- fth.compiling = true; }, 0
- , cS{";" }, [](auto) { *fth.here++ = 0; fth.compiling = false; }, word_base::immediate
- , cS{"\\" }, [](auto) { fth.sourcei = npos; }, word_base::immediate
- , cS{"cell" }, [](auto) { fth.push(sizeof(cell)); }, 0
- , cS{"_jmp" }, [](auto) {
- auto ptr = ++fth.ip;
- fth.ip = *std::bit_cast<func **>(ptr) - 1; }, 0
- , cS{"_jmp0"}, [](auto) {
- auto ptr = ++fth.ip;
- if (fth.pop() == 0)
- fth.ip = *std::bit_cast<func **>(ptr) - 1; }, 0
- , cS{"chars"}, [](auto) {}, 0
- , cS{"postpone"}, [](auto) {
- assert<error::compile_only_word>(fth.compiling);
- auto w = fth.parse();
- auto g = fth.get(w);
- assert<error::word_not_found>(g.has_value());
- *fth.here++ = std::bit_cast<cell>((*g)->body()); }, word_base::immediate
- >::word;
- constexpr static auto& dict2 = comp_dict<forth::prologue<fthp>, &dict1
- , cS{"align" }, cS{"here dup aligned swap - allot"}, 0
- , cS{"aligned"}, cS{"cell 1- + cell 1- invert and"}, 0
- , cS{"decimal"}, cS{"10 base !"}, 0
- , cS{"hex" }, cS{"16 base !"}, 0
- , cS{"<=" }, cS{"2dup < >r = r> or"}, 0
- , cS{"2!" }, cS{"swap over ! cell+ !"}, 0
- , cS{"2@" }, cS{"dup cell+ @ swap @"}, 0
- , cS{"c," }, cS{"here c! 1 allot"}, 0
- , cS{"," }, cS{"here ! cell allot"}, 0
- , cS{"allot" }, cS{"dp +!"}, 0
- , cS{"+!" }, cS{"dup >r swap r> @ + swap !"}, 0
- , cS{"2swap" }, cS{"rot >r rot r>"}, 0
- , cS{"2dup" }, cS{"over over"}, 0
- , cS{"2over" }, cS{"3 pick 3 pick"}, 0
- , cS{">r" }, cS{"rp@ cell - rp ! rp@ cell+ @ rp@ ! rp@ cell+ !"}, 0
- , cS{"r>" }, cS{"rp@ @ rp@ cell+ rp ! rp@ @ swap rp@ !"}, 0
- , cS{"over" }, cS{"1 pick"}, 0
- , cS{"pick" }, cS{"cells cell+ sp@ + @"}, 0
- , cS{"sp@" }, cS{"sp @"}, 0
- , cS{"rp@" }, cS{"rp @ cell+"}, 0
- , cS{"here" }, cS{"dp @"}, 0
- , cS{"latest"}, cS{"_d @"}, 0
- , cS{"1-" }, cS{"1 -" }, 0
- , cS{"1+" }, cS{"1 +" }, 0
- , cS{"cell+" }, cS{"cell +"}, 0
- , cS{"cells" }, cS{"cell *"}, 0
- , cS{"char+" }, cS{"1 +" }, 0
- , cS{"-rot" }, cS{"rot rot"}, 0
- , cS{"2drop" }, cS{"drop drop"}, 0
- , cS{"0=" }, cS{"0 ="}, 0
- , cS{"0<" }, cS{"0 <"}, 0
- , cS{"<>" }, cS{"= 0="}, 0
- , cS{">" }, cS{"swap <"}, 0
- , cS{"invert"}, cS{"-1 xor"}, 0
- , cS{"negate"}, cS{"-1 *"}, 0
- , cS{"2*" }, cS{"2 *"}, 0
- , cS{"bl" }, cS{"32"}, 0
- >::word;
-
- fth.next = &dict2;
- fth.end = end_value;
-}
-
-//static_assert(offsetof(word_base, flags_len) == 1 * sizeof(cell));
-//static_assert(offsetof(forth, sp) == 1 * sizeof(cell));
-//static_assert(offsetof(forth, rp) == 2 * sizeof(cell));
-//static_assert(offsetof(forth, ip) == 3 * sizeof(cell));
-//static_assert(offsetof(forth, here) == 4 * sizeof(cell));
-//static_assert(offsetof(forth, source) == 5 * sizeof(cell));
-//static_assert(offsetof(forth, sourcei) == 6 * sizeof(cell));
-//static_assert(offsetof(forth, compiling) == 7 * sizeof(cell));
-//static_assert(offsetof(forth, end) == 8 * sizeof(cell));
-//static_assert(offsetof(forth, base) == 9 * sizeof(cell));
-
-#endif // SFORTH_HPP
-
diff --git a/main.cpp b/main.cpp
index c3267ba..99f5ecc 100644
--- a/main.cpp
+++ b/main.cpp
@@ -14,7 +14,7 @@
/// You should have received a copy of the GNU General Public License along
/// with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "forth.hpp"
+#include "sforth/forth.hpp"
#include <array>
#include <fstream>
@@ -22,23 +22,23 @@
#include <span>
#include <string>
-static std::array<cell, 1024> dict;
-static auto fth = new (dict.data()) forth;
+static std::array<sforth::cell, 1024> dict;
+static auto fth = new (dict.data()) sforth::forth;
-static bool parse_stream(forth *, std::istream&, bool say_okay = false);
+static bool parse_stream(sforth::forth *, std::istream&, bool say_okay = false);
int main(int argc, const char *argv[])
{
std::span args (argv + 1, argc - 1);
- forth::initialize<&fth>(dict.end());
+ sforth::initialize<&fth>(dict.end());
fth->add(".", [](auto) {
char buf[32] = {};
std::to_chars(buf, buf + sizeof(buf), fth->pop(), fth->base);
std::cout << buf << ' ';
});
fth->add("emit", [](auto) { std::cout << static_cast<char>(fth->pop()); });
- fth->add("dictsize", [](auto) { fth->push(dict.size() * sizeof(cell)); });
+ fth->add("dictsize", [](auto) { fth->push(dict.size() * sizeof(sforth::cell)); });
for (auto arg : args) {
if (std::ifstream file {arg}; parse_stream(fth, file))
@@ -48,7 +48,7 @@ int main(int argc, const char *argv[])
parse_stream(fth, std::cin, true);
}
-bool parse_stream(forth *fth, std::istream& str, bool say_okay)
+bool parse_stream(sforth::forth *fth, std::istream& str, bool say_okay)
{
std::string line;
@@ -60,8 +60,8 @@ bool parse_stream(forth *fth, std::istream& str, bool say_okay)
try {
fth->parse_line(line);
- } catch (forth::error e) {
- std::cerr << fth->error_string(e) << " in " << line << std::endl;
+ } catch (sforth::error e) {
+ std::cerr << sforth::error_string(e) << " in " << line << std::endl;
continue;
}
}
diff --git a/sforth/comp_word.hpp b/sforth/comp_word.hpp
new file mode 100644
index 0000000..e75eee3
--- /dev/null
+++ b/sforth/comp_word.hpp
@@ -0,0 +1,92 @@
+/// sforth, an implementation of forth
+/// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
+///
+/// This program is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU 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 General Public License for
+/// more details.
+///
+/// You should have received a copy of the GNU General Public License along
+/// with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef SFORTH_COMP_WORD_HPP
+#define SFORTH_COMP_WORD_HPP
+
+#include "native_word.hpp"
+
+#include <charconv>
+
+namespace sforth {
+
+template<const func Prol, S Name, S Body, auto *Prev = (const word_base *)nullptr>
+struct comp_word : public native_word<Name, Prol, Prev>
+{
+ static constexpr std::size_t B =
+ [] {
+ std::size_t b = 1;
+ std::string_view sv {Body.data};
+ auto sourcei = sv.find_first_not_of(" \t\r\n");
+ while (sourcei != std::string_view::npos) {
+ const auto word = word_list::parse(Body.data, sourcei);
+
+ b++;
+ if (!Prev->get_ct(word))
+ b++;
+ }
+ return b;
+ }();
+
+ union bodyt {
+ const func *f;
+ cell c;
+ };
+
+ std::array<bodyt, B> bodybuf {};
+
+ consteval comp_word(addr flags = 0):
+ native_word<Name, Prol, Prev>{flags}
+ {
+ auto bptr = bodybuf.begin();
+ std::string_view sv {Body};
+ auto sourcei = sv.find_first_not_of(" \t\r\n");
+ while (sourcei != std::string_view::npos) {
+ const auto word = word_list::parse(Body, sourcei);
+
+ auto w = Prev->get_ct(word);
+ if (w) {
+ bptr->f = Prev->get_ct(word);
+ bptr++;
+ } else {
+ cell n;
+ std::from_chars(word.cbegin(), word.cend(), n, 10);
+
+ bptr->f = Prev->get_ct("_lit");
+ bptr++;
+ bptr->c = n;
+ bptr++;
+ }
+ }
+ }
+};
+
+template<func Prol, auto *Prev, S Name, S Body, addr Flags, auto... Next>
+struct comp_dict
+{
+ constexpr static comp_word<Prol, Name, Body,
+ [] {
+ if constexpr (sizeof...(Next))
+ return &comp_dict<Prol, Prev, Next...>::word;
+ else
+ return Prev;
+ }()> word {Flags};
+};
+
+} // namespace sforth
+
+#endif // SFORTH_COMP_WORD_HPP
+
diff --git a/sforth/forth.hpp b/sforth/forth.hpp
new file mode 100644
index 0000000..91c580c
--- /dev/null
+++ b/sforth/forth.hpp
@@ -0,0 +1,345 @@
+/// sforth, an implementation of forth
+/// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
+///
+/// This program is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU 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 General Public License for
+/// more details.
+///
+/// You should have received a copy of the GNU General Public License along
+/// with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef SFORTH_HPP
+#define SFORTH_HPP
+
+#include "comp_word.hpp"
+#include "types.hpp"
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <cstddef>
+#include <charconv>
+#include <string_view>
+#include <tuple>
+#include <utility>
+
+namespace sforth {
+
+constexpr bool enable_exceptions = true;
+
+enum class error
+{
+ init_error,
+ parse_error,
+ execute_error,
+ dictionary_overflow,
+ word_not_found,
+ stack_underflow,
+ stack_overflow,
+ return_stack_underflow,
+ return_stack_overflow,
+ compile_only_word
+};
+
+inline auto error_string(error err) noexcept -> std::string_view
+{
+ using enum error;
+ switch (err) {
+ case init_error: return "init error";
+ case parse_error: return "parse error";
+ case execute_error: return "execute error";
+ case dictionary_overflow: return "dictionary overflow";
+ case word_not_found: return "word not found";
+ case stack_underflow: return "stack underflow";
+ case stack_overflow: return "stack overflow";
+ case return_stack_underflow: return "return stack underflow";
+ case return_stack_overflow: return "return stack overflow";
+ case compile_only_word: return "compile only word";
+ default: return "unknown error";
+ }
+}
+
+
+template<error Err>
+inline void assert(bool condition)
+{
+ if constexpr (enable_exceptions) {
+ if (!condition)
+ throw Err;
+ }
+}
+
+struct forth : public word_list
+{
+ static constexpr int data_size = 16;
+ static constexpr int return_size = 16;
+
+ static constexpr auto npos = std::string_view::npos;
+
+ void push(cell v) {
+ assert<error::stack_overflow>(sp != dstack.begin());
+ *--sp = v;
+ }
+
+ void push(cell v, auto... vs) {
+ push(v); (push(vs), ...);
+ }
+
+ void rpush(func *v) {
+ assert<error::return_stack_overflow>(rp != rstack.begin());
+ *--rp = v;
+ }
+
+ cell& top() {
+ assert<error::stack_underflow>(sp != dstack.end());
+ return *sp;
+ }
+
+ cell pop() {
+ assert<error::stack_underflow>(sp != dstack.end());
+ return *sp++;
+ }
+
+ auto rpop() -> func * {
+ assert<error::return_stack_underflow>(rp != rstack.end());
+ return *rp++;
+ }
+
+ template<int N>
+ auto pop() {
+ static_assert(N > 0, "pop<N>() with N <= 0");
+
+ auto t = std::tuple {pop()};
+ if constexpr (N > 1)
+ return std::tuple_cat(t, pop<N - 1>());
+ else
+ return t;
+ }
+
+ forth& add(std::string_view name, func entry = nullptr) {
+ const auto namesz = (name.size() + 1 + sizeof(cell) - 1) & ~(sizeof(cell) - 1);
+ const auto size = (sizeof(word_base) + namesz) / sizeof(cell);
+
+ assert<error::parse_error>(!name.empty());
+ //assert<error::dictionary_overflow>(state->here + size < &dictionary.back());
+
+ const auto h = std::exchange(here, here + size);
+ next = new (h) word_base (next, namesz);
+ std::copy(name.begin(), name.end(),
+ std::bit_cast<char *>(h) + sizeof(word_base));
+ if (entry)
+ *here++ = std::bit_cast<cell>(entry);
+ return *this;
+ }
+
+ auto parse() -> std::string_view {
+ return word_list::parse(source, sourcei);
+ }
+
+ void parse_line(std::string_view sv) {
+ source = sv.data();
+ sourcei = sv.find_first_not_of(" \t\r\n");
+
+ while (sourcei != npos) {
+ const auto word = parse();
+
+ if (auto ent = get(word); !ent) {
+ cell n;
+ const auto [p, e] = std::from_chars(word.cbegin(), word.cend(),
+ n, base);
+
+ assert<error::word_not_found>(e == std::errc() && p == word.cend());
+
+ push(n);
+
+ if (compiling)
+ execute((*get("literal"))->body());
+ } else {
+ auto body = (*ent)->body();
+
+ if (compiling && ((*ent)->flags_len & word_base::immediate) == 0)
+ *here++ = std::bit_cast<cell>(body);
+ else
+ execute(body);
+ }
+ }
+ }
+
+ void execute(const func *body) {
+ assert<error::execute_error>(body && *body);
+ (*body)(body);
+ }
+
+ constexpr forth() {
+ sp = dstack.end();
+ rp = rstack.end();
+ }
+
+ cell *sp;
+ func **rp;
+ func *ip = nullptr;
+ cell *here = std::bit_cast<cell *>(this + 1);
+ const char *source = nullptr;
+ std::size_t sourcei = npos;
+ cell compiling = false;
+ cell *end = nullptr;
+ cell base = 10;
+ std::array<cell, data_size> dstack;
+ std::array<func *, return_size> rstack;
+};
+
+template<forth **fthp>
+void prologue(const void *bodyf)
+{
+ static auto& fth = **fthp;
+
+ auto body = (func *)bodyf;
+ fth.rpush(fth.ip);
+
+ for (fth.ip = body + 1; *fth.ip; fth.ip++)
+ fth.execute(std::bit_cast<func *>(*fth.ip));
+
+ fth.ip = fth.rpop();
+}
+
+template<forth** fthp>
+void initialize(cell *end_value)
+{
+ assert<error::init_error>(*fthp);
+
+ static auto& fth = **fthp;
+ static auto _d = std::bit_cast<cell>(*fthp);
+
+ constexpr static func lit_impl = [](auto) {
+ auto ptr = std::bit_cast<cell *>(++fth.ip);
+ fth.push(*ptr);
+ };
+
+ constexpr static auto& dict1 = native_dict<
+ S{"_d" }, [](auto) { fth.push(_d); }, 0
+ , S{"sp" }, [](auto) { fth.push(_d + sizeof(cell)); }, 0
+ , S{"rp" }, [](auto) { fth.push(_d + 2 * sizeof(cell)); }, 0
+ , S{"ip" }, [](auto) { fth.push(_d + 3 * sizeof(cell)); }, 0
+ , S{"dp" }, [](auto) { fth.push(_d + 4 * sizeof(cell)); }, 0
+ , S{"state"}, [](auto) { fth.push(_d + 7 * sizeof(cell)); }, 0
+ , S{"base" }, [](auto) { fth.push(_d + 9 * sizeof(cell)); }, 0
+ , S{"_lit" }, lit_impl, 0
+ , S{"swap" }, [](auto) { auto [a, b] = fth.pop<2>(); fth.push(a, b); }, 0
+ , S{"drop" }, [](auto) { fth.pop(); }, 0
+ , S{"dup" }, [](auto) { fth.push(fth.top()); }, 0
+ , S{"rot" }, [](auto) { auto [a, b, c] = fth.pop<3>(); fth.push(b, a, c); }, 0
+ , S{"+" }, [](auto) { fth.top() += fth.pop(); }, 0
+ , S{"-" }, [](auto) { fth.top() -= fth.pop(); }, 0
+ , S{"*" }, [](auto) { fth.top() *= fth.pop(); }, 0
+ , S{"/" }, [](auto) { fth.top() /= fth.pop(); }, 0
+ , S{"mod" }, [](auto) { fth.top() %= fth.pop(); }, 0
+ , S{"and" }, [](auto) { fth.top() &= fth.pop(); }, 0
+ , S{"or" }, [](auto) { fth.top() |= fth.pop(); }, 0
+ , S{"xor" }, [](auto) { fth.top() ^= fth.pop(); }, 0
+ , S{"lshift"}, [](auto) { fth.top() <<= fth.pop(); }, 0
+ , S{"rshift"}, [](auto) { fth.top() >>= fth.pop(); }, 0
+ , S{"[" }, [](auto) { fth.compiling = false; }, word_base::immediate
+ , S{"]" }, [](auto) { fth.compiling = true; }, 0
+ , S{"immediate"}, [](auto) { const_cast<word_base *>(fth.next)->make_immediate(); }, 0
+ , S{"literal"}, [](auto) {
+ //assert<error::compile_only_word>(fth.compiling);
+ *fth.here++ = std::bit_cast<cell>(&lit_impl);
+ *fth.here++ = fth.pop(); }, word_base::immediate
+ , S{"@" }, [](auto) { fth.push(*std::bit_cast<cell *>(fth.pop())); }, 0
+ , S{"!" }, [](auto) { auto [p, v] = fth.pop<2>(); *std::bit_cast<cell *>(p) = v; }, 0
+ , S{"c@" }, [](auto) { fth.push(*std::bit_cast<char *>(fth.pop())); }, 0
+ , S{"c!" }, [](auto) { auto [p, v] = fth.pop<2>(); *std::bit_cast<char *>(p) = v; }, 0
+ , S{"=" }, [](auto) { auto v = fth.pop(); fth.top() = -(fth.top() == v); }, 0
+ , S{"<" }, [](auto) { auto v = fth.pop(); fth.top() = -(fth.top() < v); }, 0
+ , S{"\'" }, [](auto) {
+ auto w = fth.parse();
+ auto g = fth.get(w);
+ fth.push(g ? std::bit_cast<cell>((*g)->body()) : 0); }, 0
+ , S{":" }, [](auto) {
+ auto w = fth.parse();
+ fth.add(w);
+ *fth.here++ = std::bit_cast<cell>(&prologue<fthp>);
+ fth.compiling = true; }, 0
+ , S{";" }, [](auto) { *fth.here++ = 0; fth.compiling = false; }, word_base::immediate
+ , S{"\\" }, [](auto) { fth.sourcei = forth::npos; }, word_base::immediate
+ , S{"cell" }, [](auto) { fth.push(sizeof(cell)); }, 0
+ , S{"_jmp" }, [](auto) {
+ auto ptr = ++fth.ip;
+ fth.ip = *std::bit_cast<func **>(ptr) - 1; }, 0
+ , S{"_jmp0"}, [](auto) {
+ auto ptr = ++fth.ip;
+ if (fth.pop() == 0)
+ fth.ip = *std::bit_cast<func **>(ptr) - 1; }, 0
+ , S{"chars"}, [](auto) {}, 0
+ , S{"postpone"}, [](auto) {
+ assert<error::compile_only_word>(fth.compiling);
+ auto w = fth.parse();
+ auto g = fth.get(w);
+ assert<error::word_not_found>(g.has_value());
+ *fth.here++ = std::bit_cast<cell>((*g)->body()); }, word_base::immediate
+ >::word;
+ constexpr static auto& dict2 = comp_dict<prologue<fthp>, &dict1
+ , S{"align" }, S{"here dup aligned swap - allot"}, 0
+ , S{"aligned"}, S{"cell 1- + cell 1- invert and"}, 0
+ , S{"decimal"}, S{"10 base !"}, 0
+ , S{"hex" }, S{"16 base !"}, 0
+ , S{"<=" }, S{"2dup < >r = r> or"}, 0
+ , S{"2!" }, S{"swap over ! cell+ !"}, 0
+ , S{"2@" }, S{"dup cell+ @ swap @"}, 0
+ , S{"c," }, S{"here c! 1 allot"}, 0
+ , S{"," }, S{"here ! cell allot"}, 0
+ , S{"allot" }, S{"dp +!"}, 0
+ , S{"+!" }, S{"dup >r swap r> @ + swap !"}, 0
+ , S{"2swap" }, S{"rot >r rot r>"}, 0
+ , S{"2dup" }, S{"over over"}, 0
+ , S{"2over" }, S{"3 pick 3 pick"}, 0
+ , S{">r" }, S{"rp@ cell - rp ! rp@ cell+ @ rp@ ! rp@ cell+ !"}, 0
+ , S{"r>" }, S{"rp@ @ rp@ cell+ rp ! rp@ @ swap rp@ !"}, 0
+ , S{"over" }, S{"1 pick"}, 0
+ , S{"pick" }, S{"cells cell+ sp@ + @"}, 0
+ , S{"sp@" }, S{"sp @"}, 0
+ , S{"rp@" }, S{"rp @ cell+"}, 0
+ , S{"here" }, S{"dp @"}, 0
+ , S{"latest"}, S{"_d @"}, 0
+ , S{"1-" }, S{"1 -" }, 0
+ , S{"1+" }, S{"1 +" }, 0
+ , S{"cell+" }, S{"cell +"}, 0
+ , S{"cells" }, S{"cell *"}, 0
+ , S{"char+" }, S{"1 +" }, 0
+ , S{"-rot" }, S{"rot rot"}, 0
+ , S{"2drop" }, S{"drop drop"}, 0
+ , S{"0=" }, S{"0 ="}, 0
+ , S{"0<" }, S{"0 <"}, 0
+ , S{"<>" }, S{"= 0="}, 0
+ , S{">" }, S{"swap <"}, 0
+ , S{"invert"}, S{"-1 xor"}, 0
+ , S{"negate"}, S{"-1 *"}, 0
+ , S{"2*" }, S{"2 *"}, 0
+ , S{"bl" }, S{"32"}, 0
+ >::word;
+
+ fth.next = &dict2;
+ fth.end = end_value;
+}
+
+} // namespace sforth
+
+//static_assert(offsetof(word_base, flags_len) == 1 * sizeof(cell));
+//static_assert(offsetof(forth, sp) == 1 * sizeof(cell));
+//static_assert(offsetof(forth, rp) == 2 * sizeof(cell));
+//static_assert(offsetof(forth, ip) == 3 * sizeof(cell));
+//static_assert(offsetof(forth, here) == 4 * sizeof(cell));
+//static_assert(offsetof(forth, source) == 5 * sizeof(cell));
+//static_assert(offsetof(forth, sourcei) == 6 * sizeof(cell));
+//static_assert(offsetof(forth, compiling) == 7 * sizeof(cell));
+//static_assert(offsetof(forth, end) == 8 * sizeof(cell));
+//static_assert(offsetof(forth, base) == 9 * sizeof(cell));
+
+#endif // SFORTH_HPP
+
diff --git a/sforth/native_word.hpp b/sforth/native_word.hpp
new file mode 100644
index 0000000..da69b09
--- /dev/null
+++ b/sforth/native_word.hpp
@@ -0,0 +1,64 @@
+/// sforth, an implementation of forth
+/// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
+///
+/// This program is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU 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 General Public License for
+/// more details.
+///
+/// You should have received a copy of the GNU General Public License along
+/// with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef SFORTH_NATIVE_WORD_HPP
+#define SFORTH_NATIVE_WORD_HPP
+
+#include "types.hpp"
+
+#include <array>
+
+namespace sforth {
+
+template<S Name, func Body, auto *Prev = (const word_base *)nullptr>
+struct native_word : public word_base
+{
+ constexpr static auto N = (sizeof(Name) + sizeof(cell) - 1) & ~(sizeof(cell) - 1);
+ std::array<char, N> namebuf;
+ func body;
+
+ consteval const func *get_ct(std::string_view name) const {
+ if (name == std::string_view{Name.data})
+ return &body;
+ else if constexpr (Prev != nullptr)
+ return Prev->get_ct(name);
+ else
+ return nullptr;
+ }
+
+ consteval native_word(addr flags = 0):
+ word_base{Prev, N | flags}, namebuf{}, body{Body}
+ {
+ std::copy(Name.data, Name.data + sizeof(Name), namebuf.data());
+ }
+};
+
+template<S Name, func Body, addr Flags, auto... Next>
+struct native_dict
+{
+ constexpr static native_word<Name, Body,
+ [] {
+ if constexpr (sizeof...(Next))
+ return &native_dict<Next...>::word;
+ else
+ return (const word_base *)nullptr;
+ }()> word {Flags};
+};
+
+} // namespace sforth
+
+#endif // SFORTH_NATIVE_WORD_HPP
+
diff --git a/sforth/types.hpp b/sforth/types.hpp
new file mode 100644
index 0000000..d908b8f
--- /dev/null
+++ b/sforth/types.hpp
@@ -0,0 +1,106 @@
+/// sforth, an implementation of forth
+/// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
+///
+/// This program is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU 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 General Public License for
+/// more details.
+///
+/// You should have received a copy of the GNU General Public License along
+/// with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#ifndef SFORTH_TYPES_HPP
+#define SFORTH_TYPES_HPP
+
+#include <algorithm>
+#include <bit>
+#include <cstdint>
+#include <optional>
+#include <string_view>
+
+namespace sforth {
+
+using cell = std::intptr_t;
+using addr = std::uintptr_t;
+using func = void (*)(const void *);
+
+struct word_base;
+
+struct word_list
+{
+ const word_base *next;
+
+ constexpr word_list(const word_base *n = nullptr): next{n} {}
+
+ std::optional<const word_base *> get(std::string_view sv) const;
+
+ static constexpr auto parse(const char *source, std::size_t& sourcei) -> std::string_view {
+ const std::string_view sv {source};
+
+ const auto e = sv.find_first_of(" \t\r\n", sourcei);
+ const auto word = e != std::string_view::npos ?
+ sv.substr(sourcei, e - sourcei) : sv.substr(sourcei);
+
+ sourcei = sv.find_first_not_of(" \t\r\n", e);
+ return word;
+ }
+};
+
+struct word_base : public word_list
+{
+ static constexpr addr immediate = 1 << 8;
+
+ addr flags_len;
+
+ constexpr word_base(const word_base *n, addr fl):
+ word_list{n}, flags_len{fl} {}
+
+ std::string_view name() const {
+ return {std::bit_cast<const char *>(this + 1)};
+ }
+
+ const func *body() const {
+ const auto ptr = std::bit_cast<const std::uint8_t *>(this + 1);
+ const auto fptr = ptr + (flags_len & 0xFF);
+ return std::bit_cast<const func *>(fptr);
+ }
+
+ constexpr void make_immediate() {
+ flags_len |= immediate;
+ }
+};
+
+std::optional<const word_base *> word_list::get(std::string_view sv) const
+{
+ for (auto lt = next; lt; lt = lt->next) {
+ if (sv == lt->name())
+ return lt;
+ }
+
+ return {};
+}
+
+template<unsigned N>
+struct S {
+ char data[N];
+
+ consteval S(const char (&s)[N]) {
+ std::copy(s, s + N, data);
+ }
+ consteval operator const char *() const {
+ return data;
+ }
+ consteval auto size() const {
+ return N;
+ }
+};
+
+} // namespace sforth
+
+#endif // SFORTH_TYPES_HPP
+