refactor, encapsulate

main
Clyne 5 months ago
parent b82f1c1f7e
commit a79eaf8e16
Signed by: clyne
GPG Key ID: 1B74EE6C49C96795

@ -1,7 +1,7 @@
SRC := $(wildcard source/*.cpp) SRC := $(wildcard source/*.cpp)
OBJ := $(subst .cpp,.o,$(SRC)) OBJ := $(subst .cpp,.o,$(SRC))
CXXFLAGS += -std=c++20 -ggdb -g3 -O0 \ CXXFLAGS += -std=c++20 -ggdb -g3 -Os \
-Wall -Wextra -pedantic \ -Wall -Wextra -pedantic \
-Isource -Isource

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -16,36 +16,35 @@
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#include "core.hpp" #include "core.hpp"
#include "state.hpp"
#include <cctype> #include <cctype>
#include <cstring> #include <cstring>
static State state;
void jump(FuncList ip) void jump(FuncList ip)
{ {
// IP is incremented before its next execution. // IP is incremented before its next execution.
IP = ip - 1; Exec.ip = ip - 1;
} }
// LITERAL's run-time semantics: push the given value onto the stack.
static auto literall = WordWrap<[] {
push((Cell)*++IP);
}>;
void compileliteral() void compileliteral()
{ {
comma((Cell)literall); // LITERAL's run-time semantics: push the given value onto the stack.
comma(pop()); comma((Cell)WordWrap<[] {
Forth.push((Cell)*++Exec.ip);
}>);
comma(Forth.pop());
} }
bool haskey() bool haskey()
{ {
return *((char *)&DICT[DIdxInBuf]) < DICT[DIdxSrcLen]; return Forth.sourcei < Forth.sourceu;
} }
void addkey(int k) void addkey(int k)
{ {
auto addr = DICT[DIdxSource] + (DICT[DIdxSrcLen]++); auto addr = Forth.source + Forth.sourceu++;
auto ptr = reinterpret_cast<char *>(addr); auto ptr = reinterpret_cast<char *>(addr);
*ptr = static_cast<char>(k); *ptr = static_cast<char>(k);
} }
@ -56,16 +55,16 @@ int key()
while (!haskey()) while (!haskey())
getinput(); getinput();
auto ptr = reinterpret_cast<char *>(DICT[DIdxSource]); auto ptr = reinterpret_cast<char *>(Forth.source);
int idx = (*((char *)&DICT[DIdxInBuf]))++; int idx = Forth.sourcei++;
return ptr[idx]; return ptr[idx];
} }
Cell *comma(Cell n) Cell *comma(Cell n)
{ {
const auto ptr = reinterpret_cast<Cell *>(HERE); const auto ptr = reinterpret_cast<Cell *>(Forth.here);
*ptr = n; *ptr = n;
HERE += sizeof(Cell); Forth.here += sizeof(Cell);
return ptr; return ptr;
} }
@ -76,7 +75,7 @@ Addr aligned(Addr addr)
void align() void align()
{ {
HERE = aligned(HERE); Forth.here = aligned(Forth.here);
} }
static void readword(int ch) static void readword(int ch)
@ -89,9 +88,9 @@ static void readword(int ch)
// Collect the word's text. // Collect the word's text.
char *ptr; char *ptr;
do { do {
ptr = reinterpret_cast<char *>(HERE); ptr = reinterpret_cast<char *>(Forth.here);
*ptr = k; *ptr = k;
++HERE; ++Forth.here;
if (!haskey()) if (!haskey())
break; break;
@ -100,35 +99,36 @@ static void readword(int ch)
} while (k != ch); } while (k != ch);
// Add a null terminator. // Add a null terminator.
ptr = reinterpret_cast<char *>(HERE); ptr = reinterpret_cast<char *>(Forth.here);
*ptr = '\0'; *ptr = '\0';
++HERE; ++Forth.here;
} }
void word() void word()
{ {
auto here = (char *)HERE; auto here = (char *)Forth.here;
++HERE; ++Forth.here;
readword(*sp()); readword(*Forth.sp);
here[0] = strlen(here + 1); here[0] = strlen(here + 1);
HERE = (Cell)here; Forth.here = (Cell)here;
*sp() = HERE; *Forth.sp = Forth.here;
} }
void colon() void colon()
{ {
// Collect (and store) the word's name. // Collect (and store) the word's name.
align(); align();
auto name = HERE; auto name = Forth.here;
readword(' '); readword(' ');
align(); align();
// Build the Word structure. // Build the Word structure.
comma(HERE + 4 * sizeof(Cell)); // exec ptr Forth.push(Forth.here);
comma(Forth.here + 4 * sizeof(Cell)); // exec ptr
comma(name); // name ptr comma(name); // name ptr
push((Cell)comma(0)); // link (to be set by semic()) comma(0); // link
comma(0); // immediate comma(0); // immediate
// The word's execution begins with a prologue that technically performs // The word's execution begins with a prologue that technically performs
@ -137,16 +137,15 @@ void colon()
// about if it is running words or routines (i.e. pre-defined words). // about if it is running words or routines (i.e. pre-defined words).
comma((Cell)+[](FuncList *ip) { comma((Cell)+[](FuncList *ip) {
++ip; ++ip;
rpush((Cell)IP); Forth.rpush((Cell)Exec.ip);
jump((FuncList)*ip); jump((FuncList)*ip);
}); });
// The actual function list will begin one Cell beyond here. // The actual function list will begin one Cell beyond here.
comma(HERE + sizeof(Cell)); comma(Forth.here + sizeof(Cell));
DICT[DIdxCompXt] = *sp() - 2 * sizeof(Cell);
// Enter compiling state. // Enter compiling state.
STATE = -1; Forth.compxt = *Forth.sp;
Forth.state = -1;
} }
void semic() void semic()
@ -155,27 +154,26 @@ void semic()
comma((Cell)fexit); comma((Cell)fexit);
// Complete the new word's linkage to make it usable. // Complete the new word's linkage to make it usable.
auto link = (Cell *)pop(); auto word = reinterpret_cast<Word *>(Forth.pop());
*link = LATEST; Forth.add(*word);
LATEST = (Cell)(link - 2);
// Exit compilation state. // Exit compilation state.
STATE = 0; Forth.state = 0;
} }
// TODO define in Forth? ": ' bl word find drop ;" // TODO define in Forth? ": ' bl word find drop ;"
void tick() void tick()
{ {
// Get the name to look up. // Get the name to look up.
auto name = (char *)HERE; auto name = Forth.here;
readword(' '); readword(' ');
// Look up the name and push the result. // Look up the name and push the result.
int len = HERE - (Cell)name - 1; int len = Forth.here - name - 1;
auto word = find(name, len); auto word = Forth.find((char *)name, len);
push((Cell)word); Forth.push((Cell)word);
// Deallocate `name`. // Deallocate `name`.
HERE = (Cell)name; Forth.here = name;
} }

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -18,7 +18,8 @@
#ifndef CORE_HPP #ifndef CORE_HPP
#define CORE_HPP #define CORE_HPP
#include "types.hpp" #include "executor.hpp"
#include "state.hpp"
/** /**
* To be implemented by the user: Adds available input to the source buffer. * To be implemented by the user: Adds available input to the source buffer.
@ -30,9 +31,7 @@ extern void getinput();
* "Function exit" word, analagous to a function's return statement. * "Function exit" word, analagous to a function's return statement.
*/ */
constexpr auto fexit = WordWrap<[] { constexpr auto fexit = WordWrap<[] {
extern FuncList IP; Exec.ip = reinterpret_cast<FuncList>(Forth.rpop());
extern Cell rpop();
IP = reinterpret_cast<FuncList>(rpop());
}>; }>;
void jump(FuncList ip); /** Jumps to the given instruction pointer. */ void jump(FuncList ip); /** Jumps to the given instruction pointer. */

@ -0,0 +1,75 @@
// sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// This library 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 Library General Public License for
// more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#include "executor.hpp"
Executor Exec;
Error Executor::executor(FuncList *list) noexcept
{
auto result = static_cast<Error>(setjmp(jmpbuf));
FuncList body;
if (static_cast<int>(result) == 0) {
result = Error::none;
// We are given the pointer to a list of function pointers.
// Dereference once to retrieve the function pointer list.
// We do not work with IP initially since it needs to be set to zero if
// this is a top-level call/execution.
body = *list;
// Enter the execution loop.
goto entry;
// Execution continues so long as IP is not zero.
// If this is a top-level execution of a pre-defined word, then IP will
// remain zero'd and the loop will immediately exit.
// If this is a defined word's execution, then its "call" will overwrite
// IP (and push the initial zero-IP to the return stack); execution will
// continue until we return to the zero-IP.
while (ip) {
// Retrieve next function pointer list.
body = (FuncList)*++ip;
entry:
// Dereference `body` to get the first function in the list.
// This is casted to take a FuncList as an argument since defined
// words need to know their addresses so that they can perform
// their "calls".
// If the word is pre-defined then the argument will simply be
// ignored.
auto func = (void (*)(FuncList))*body;
func(body);
}
}
return result;
}
Error Executor::execute1(Word *word) noexcept
{
// IP must initially be zero if executing a word at the top level.
ip = nullptr;
return executor(&word->list);
}
[[noreturn]]
void Executor::terminate(Error error) noexcept
{
std::longjmp(jmpbuf, static_cast<int>(error));
}

@ -0,0 +1,53 @@
// sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by
// the Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// This library 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 Library General Public License for
// more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef EXECUTOR_HPP
#define EXECUTOR_HPP
#include "types.hpp"
#include <csetjmp>
class Executor
{
std::jmp_buf jmpbuf;
public:
FuncList ip = nullptr;
/**
* Begins execution with the given function pointer list.
* @param list Function pointer list to execute
*/
[[nodiscard]]
Error executor(FuncList *list) noexcept;
/**
* Executes the given word by calling executor on its definition.
* @param word The word to execute
*/
[[nodiscard]]
Error execute1(Word *word) noexcept;
[[noreturn]]
void terminate(Error error) noexcept;
};
extern Executor Exec;
#endif // EXECUTOR_HPP

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -16,6 +16,7 @@
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#include "core.hpp" #include "core.hpp"
#include "executor.hpp"
#include "state.hpp" #include "state.hpp"
#include "types.hpp" #include "types.hpp"
@ -34,16 +35,16 @@ static Error parseword(const char *start, const char *end)
auto result = Error::none; auto result = Error::none;
if (start != end) { if (start != end) {
if (auto word = find(start, end - start); word) { if (auto word = Forth.find(start, end - start); word) {
if (!word->immediate() && STATE) { if (!word->immediate() && Forth.state) {
comma((Cell)word->list); comma((Cell)word->list);
} else { } else {
result = execute1(word); result = Exec.execute1(word);
} }
} else if (isdigit(*start) || (*start == '-' && isdigit(*(start + 1)))) { } else if (isdigit(*start) || (*start == '-' && isdigit(*(start + 1)))) {
push(std::atoi(start)); Forth.push(std::atoi(start));
if (STATE) if (Forth.state)
compileliteral(); compileliteral();
} else if (findword(start, end - start)) { } else if (findword(start, end - start)) {
return Error::noword; return Error::noword;
@ -57,11 +58,11 @@ static Error parseword(const char *start, const char *end)
static Error parseSource() static Error parseSource()
{ {
auto result = Error::none; auto result = Error::none;
char *start = (char *)DICT[DIdxSource]; char *start = (char *)Forth.source;
char *end = start; char *end = start;
auto& i = *((char *)&DICT[DIdxInBuf]); auto& i = *((char *)&Forth.sourcei);
while (result == Error::none && i < DICT[DIdxSrcLen]) { while (result == Error::none && i < Forth.sourceu) {
if (isspace(*end) || *end == '\0') { if (isspace(*end) || *end == '\0') {
if (end - start < 1) { if (end - start < 1) {
start = ++end; start = ++end;
@ -69,7 +70,7 @@ static Error parseSource()
} else { } else {
++i; // discard the space ++i; // discard the space
result = parseword(start, end); result = parseword(start, end);
start = (char *)DICT[DIdxSource] + i; start = (char *)Forth.source + i;
end = start; end = start;
} }
} else { } else {
@ -85,9 +86,9 @@ static Error parseSource()
Error parse() Error parse()
{ {
// Reset source buffer and try to fill it with getinput(). // Reset source buffer and try to fill it with getinput().
DICT[DIdxSource] = (Cell)&DICT[DIdxInBuf] + 1; Forth.source = (Cell)&Forth.sourcei + 1;
DICT[DIdxSrcLen] = 0; Forth.sourceu = 0;
*((char *)&DICT[DIdxInBuf]) = 0; Forth.sourcei = 0;
getinput(); getinput();
addkey('\0'); addkey('\0');

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -18,108 +18,41 @@
#include "core.hpp" #include "core.hpp"
#include "state.hpp" #include "state.hpp"
#include <csetjmp>
#include <cstring> #include <cstring>
FuncList IP = nullptr; State Forth;
std::array<Cell, DictSize> DICT;
Cell& BASE = DICT[DIdxBase]; void State::push(Cell value) {
Cell& HERE = DICT[DIdxHere]; if (sp >= ds.end())
Cell& LATEST = DICT[DIdxLatest]; Exec.terminate(Error::push);
Cell& STATE = DICT[DIdxState];
static std::jmp_buf jmpbuf; *++sp = value;
static Cell *SP = DICT.data() + DICT.size() - DS;
static Cell *RP = DICT.data() + DICT.size() - DS - RS;
void push(Cell value) {
if (SP >= DICT.data() + DICT.size())
std::longjmp(jmpbuf, static_cast<int>(Error::push));
*++SP = value;
}
Cell pop() {
if (SP - 1 < DICT.data() + DICT.size() - DS)
std::longjmp(jmpbuf, static_cast<int>(Error::pop));
return *SP--;
} }
Cell *sp() { Cell State::pop() {
return SP; if (sp - 1 < ds.begin())
} Exec.terminate(Error::pop);
void rpush(Cell value) {
if (RP >= DICT.data() + DICT.size() - DS)
std::longjmp(jmpbuf, static_cast<int>(Error::rpush));
*++RP = value; return *sp--;
} }
Cell rpop() { void State::rpush(Cell value) {
if (RP - 1 < DICT.data() + DICT.size() - DS - RS) if (rp >= rs.end())
std::longjmp(jmpbuf, static_cast<int>(Error::rpop)); Exec.terminate(Error::rpush);
return *RP--; *++rp = value;
} }
Cell *rp() { Cell State::rpop() {
return RP; if (rp - 1 < rs.begin())
} Exec.terminate(Error::rpop);
Error executor(FuncList *list)
{
auto result = static_cast<Error>(setjmp(jmpbuf));
FuncList body;
if (static_cast<int>(result) == 0) { return *rp--;
result = Error::none;
// We are given the pointer to a list of function pointers.
// Dereference once to retrieve the function pointer list.
// We do not work with IP initially since it needs to be set to zero if
// this is a top-level call/execution.
body = *list;
// Enter the execution loop.
goto entry;
// Execution continues so long as IP is not zero.
// If this is a top-level execution of a pre-defined word, then IP will
// remain zero'd and the loop will immediately exit.
// If this is a defined word's execution, then its "call" will overwrite
// IP (and push the initial zero-IP to the return stack); execution will
// continue until we return to the zero-IP.
while (IP) {
// Retrieve next function pointer list.
body = (FuncList)*++IP;
entry:
// Dereference `body` to get the first function in the list.
// This is casted to take a FuncList as an argument since defined
// words need to know their addresses so that they can perform
// their "calls".
// If the word is pre-defined then the argument will simply be
// ignored.
auto func = (void (*)(FuncList))*body;
func(body);
}
}
return result;
}
Error execute1(Word *word)
{
// IP must initially be zero if executing a word at the top level.
IP = 0;
return executor(&word->list);
} }
Word *find(const char *s, int len) Word *State::find(const char *s, int len)
{ {
for (auto w = (Word *)LATEST; w; w = w->link) { for (auto w = latest; w; w = w->link) {
if (len == (int)strlen(w->name) && strncmp(s, w->name, len) == 0) if (len == (int)strlen(w->name) && strncmp(s, w->name, len) == 0)
return w; return w;
} }

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -34,58 +34,60 @@ constexpr Addr DIdxSrcLen = 6;
constexpr Addr DIdxInBuf = 7; constexpr Addr DIdxInBuf = 7;
constexpr Addr DIdxBegin = DIdxInBuf + 80 * sizeof(char); constexpr Addr DIdxBegin = DIdxInBuf + 80 * sizeof(char);
/** class State
{
/**
* Memory chunk used to store the dictoinary and stacks. * Memory chunk used to store the dictoinary and stacks.
*/ */
extern std::array<Cell, DictSize> DICT; std::array<Cell, DictSize> dict;
std::array<Cell, DS> ds;
std::array<Cell, RS> rs;
extern Cell& BASE; public:
extern Cell& HERE; /** Linked to HERE's storage in DICT. */ Cell *sp = ds.begin();
extern Cell& LATEST; /** Linked to LATEST's storage in DICT. */ Cell *rp = rs.begin();
extern Cell& STATE; /** Linked to STATE's storage in DICT. */ Cell& base = dict[DIdxBase];
extern FuncList IP; /** Instruction pointer */ Cell& here = dict[DIdxHere];
Word *& latest = *(Word **)&dict[DIdxLatest];
Cell& state = dict[DIdxState];
Cell& compxt = dict[DIdxCompXt];
Cell& source = dict[DIdxSource];
Cell& sourceu = dict[DIdxSrcLen];
unsigned char& sourcei = *(unsigned char *)&dict[DIdxInBuf];
void push(Cell); State() {
[[nodiscard]] Cell pop(); dict[DIdxBase] = 10;
[[nodiscard]] Cell *sp(); dict[DIdxHere] = (Cell)&dict[DIdxBegin];
dict[DIdxLatest] = 0;//(Cell)&wordset.back();
dict[DIdxState] = 0;
}
void rpush(Cell); void add(Word& word) noexcept {
[[nodiscard]] Cell rpop(); word.link = latest;
[[nodiscard]] Cell *rp(); latest = &word;
}
/** auto dictdata() noexcept {
* Initializes the dictionary to default values. return dict.data();
* @param wordset The initial WordSet of pre-defined words. }
*/
inline void initialize(const auto& wordset)
{
DICT[DIdxBase] = 10;
DICT[DIdxHere] = (Cell)&DICT[DIdxBegin];
DICT[DIdxLatest] = (Cell)wordset.latest;
DICT[DIdxState] = 0;
}
/** void push(Cell);
* Begins execution with the given function pointer list. [[nodiscard]] Cell pop();
* @param list Function pointer list to execute
*/
[[nodiscard]]
Error executor(FuncList *list);
/** void rpush(Cell);
* Executes the given word by calling executor on its definition. [[nodiscard]] Cell rpop();
* @param word The word to execute
*/
[[nodiscard]]
Error execute1(Word *word);
/** /**
* Looks up the definition of the given word. * Looks up the definition of the given word.
* @param s The name of the word to find * @param s The name of the word to find
* @param len The character count of the word's name * @param len The character count of the word's name
* @return Pointer to the word's definition or nullptr if not found * @return Pointer to the word's definition or nullptr if not found
*/ */
Word *find(const char *s, int len); [[nodiscard]]
Word *find(const char *s, int len);
};
extern State Forth;
#endif // STATE_HPP #endif // STATE_HPP

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -21,6 +21,7 @@
#include <array> #include <array>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory>
using Cell = intptr_t; /** Type of a dictionary's cell. */ using Cell = intptr_t; /** Type of a dictionary's cell. */
using Addr = uintptr_t; /** Type of a dictionary address. */ using Addr = uintptr_t; /** Type of a dictionary address. */
@ -33,6 +34,16 @@ using FuncList = Func const *; /** Words are defined as a list of Func. */
static_assert(sizeof(Cell) == sizeof(Addr)); static_assert(sizeof(Cell) == sizeof(Addr));
static_assert(sizeof(Cell) == sizeof(Func)); static_assert(sizeof(Cell) == sizeof(Func));
/**
* Wraps the given functions so that a function pointer list can be created
* and used for a word's definition.
*/
template<auto... funcs>
constexpr auto WordWrap = []() noexcept {
constexpr static auto wrapper = +[] { (funcs(), ...); };
return &wrapper;
}();
/** /**
* @struct Word * @struct Word
* @brief Structure of a word's definition. * @brief Structure of a word's definition.
@ -69,42 +80,6 @@ static_assert(offsetof(Word, link) == 2 * sizeof(Cell));
static_assert(offsetof(Word, imm) == 3 * sizeof(Cell)); static_assert(offsetof(Word, imm) == 3 * sizeof(Cell));
static_assert(sizeof(Word) == 4 * sizeof(Cell)); static_assert(sizeof(Word) == 4 * sizeof(Cell));
/**
* @struct WordSet
* @brief Groups a set of pre-defined words and links them.
*/
template<typename... Words>
struct WordSet
{
std::array<Word, sizeof...(Words)> words;
Word *latest;
/**
* Stores the given pre-defined words into the `words` array and links
* their definitions together. The resulting `latest` value can be used
* to set the global LATEST.
*/
constexpr WordSet(Words... ws):
words {ws...}
{
auto it = words.begin();
while (++it != words.end())
it->link = it - 1;
latest = &*words.rbegin();
}
};
/**
* Wraps the given functions so that a function pointer list can be created
* and used for a word's definition.
*/
template<auto... funcs>
constexpr auto WordWrap = []() noexcept {
constexpr static auto wrapper = +[] { (funcs(), ...); };
return &wrapper;
}();
enum class Error : int { enum class Error : int {
none = 1, none = 1,
push, push,

@ -1,5 +1,5 @@
// sprit-forth: A portable subroutine-threaded Forth. // sprit-forth: A portable subroutine-threaded Forth.
// Copyright (C) 2023 Clyne Sullivan <clyne@bitgloo.com> // Copyright (C) 2024 Clyne Sullivan <clyne@bitgloo.com>
// //
// This library is free software; you can redistribute it and/or modify it // This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Library General Public License as published by // under the terms of the GNU Library General Public License as published by
@ -27,66 +27,66 @@
// TODO: // TODO:
// sys m* _/ _% _' depth _rdepth _in _ev find _uma u< um/mod // sys m* _/ _% _' depth _rdepth _in _ev find _uma u< um/mod
static void peek() { *sp() = *(Cell *)(*sp()); } static void peek() { *Forth.sp = *(Cell *)(*Forth.sp); }
static void commaSP() { comma(pop()); } static void commaSP() { comma(Forth.pop()); }
static void discard() { auto v = pop(); (void)v; } static void discard() { auto v = Forth.pop(); (void)v; }
static void tobool() { if (*sp()) *sp() = -1; } static void tobool() { if (*Forth.sp) *Forth.sp = -1; }
constinit WordSet words ( constinit std::array words {
Word("[", WordWrap<[] { STATE = 0; }>).markImmediate(), Word("[", WordWrap<[] { Forth.state = 0; }>).markImmediate(),
Word("]", WordWrap<[] { STATE = -1; }>), Word("]", WordWrap<[] { Forth.state = -1; }>),
Word("@", WordWrap<peek>), Word("@", WordWrap<peek>),
Word("c@", WordWrap<peek, [] { *sp() &= 0xFF; }>), Word("c@", WordWrap<peek, [] { *Forth.sp &= 0xFF; }>),
Word("!", WordWrap<[] { auto a = (Cell *)pop(); *a = pop(); }>), Word("!", WordWrap<[] { auto a = (Cell *)Forth.pop(); *a = Forth.pop(); }>),
Word("c!", WordWrap<[] { auto a = (char *)pop(); *a = pop(); }>), Word("c!", WordWrap<[] { auto a = (char *)Forth.pop(); *a = Forth.pop(); }>),
Word("_d", WordWrap<[] { *sp() += (Cell)DICT.data(); }>), Word("_d", WordWrap<[] { *Forth.sp += (Cell)Forth.dictdata(); }>),
Word("_jmp", WordWrap<[] { jump((FuncList)*++IP); }>), Word("_jmp", WordWrap<[] { jump((FuncList)*++Exec.ip); }>),
Word("_jmp0", WordWrap<[] { Word("_jmp0", WordWrap<[] {
++IP; ++Exec.ip;
if (pop() == 0) if (Forth.pop() == 0)
jump((FuncList)*IP); jump((FuncList)*Exec.ip);
}>), }>),
Word(",", WordWrap<commaSP>), Word(",", WordWrap<commaSP>),
Word("emit", WordWrap<[] { std::putchar(pop()); }>), Word("emit", WordWrap<[] { std::putchar(Forth.pop()); }>),
Word("key", WordWrap<[] { push(key()); }>), Word("key", WordWrap<[] { Forth.push(key()); }>),
Word("key?", WordWrap<[] { push(haskey()); }, tobool>), Word("key?", WordWrap<[] { Forth.push(haskey()); }, tobool>),
Word("execute", WordWrap<[] { (void)executor((FuncList *)pop()); }>), Word("execute", WordWrap<[] { (void)Exec.executor((FuncList *)Forth.pop()); }>),
Word(":", WordWrap<colon>), Word(":", WordWrap<colon>),
Word(";", WordWrap<semic>).markImmediate(), Word(";", WordWrap<semic>).markImmediate(),
Word("exit", fexit), Word("exit", fexit),
Word("drop", WordWrap<discard>), Word("drop", WordWrap<discard>),
Word("dup", WordWrap<[] { push(*sp()); }>), Word("dup", WordWrap<[] { Forth.push(*Forth.sp); }>),
Word("swap", WordWrap<[] { std::swap(*sp(), *(sp() - 1)); }>), Word("swap", WordWrap<[] { std::swap(*Forth.sp, *(Forth.sp - 1)); }>),
Word("pick", WordWrap<[] { auto t = *(sp() - *sp() - 1); *sp() = t; }>), Word("pick", WordWrap<[] { auto t = *(Forth.sp - *Forth.sp - 1); *Forth.sp = t; }>),
Word("cells", WordWrap<[] { *sp() *= sizeof(Cell); }>), Word("cells", WordWrap<[] { *Forth.sp *= sizeof(Cell); }>),
Word("+", WordWrap<[] { *(sp() - 1) += *sp(); }, discard>), Word("+", WordWrap<[] { *(Forth.sp - 1) += *Forth.sp; }, discard>),
Word("-", WordWrap<[] { *(sp() - 1) -= *sp(); }, discard>), Word("-", WordWrap<[] { *(Forth.sp - 1) -= *Forth.sp; }, discard>),
Word("*", WordWrap<[] { *(sp() - 1) *= *sp(); }, discard>), Word("*", WordWrap<[] { *(Forth.sp - 1) *= *Forth.sp; }, discard>),
Word("/", WordWrap<[] { *(sp() - 1) /= *sp(); }, discard>), Word("/", WordWrap<[] { *(Forth.sp - 1) /= *Forth.sp; }, discard>),
Word("mod", WordWrap<[] { *(sp() - 1) %= *sp(); }, discard>), Word("mod", WordWrap<[] { *(Forth.sp - 1) %= *Forth.sp; }, discard>),
Word("=", WordWrap<[] { *(sp() - 1) = *(sp() - 1) == *sp(); }, discard, tobool>), Word("=", WordWrap<[] { *(Forth.sp - 1) = *(Forth.sp - 1) == *Forth.sp; }, discard, tobool>),
Word("<", WordWrap<[] { *(sp() - 1) = *(sp() - 1) < *sp(); }, discard, tobool>), Word("<", WordWrap<[] { *(Forth.sp - 1) = *(Forth.sp - 1) < *Forth.sp; }, discard, tobool>),
Word("or", WordWrap<[] { *(sp() - 1) |= *sp(); }, discard>), Word("or", WordWrap<[] { *(Forth.sp - 1) |= *Forth.sp; }, discard>),
Word("and", WordWrap<[] { *(sp() - 1) &= *sp(); }, discard>), Word("and", WordWrap<[] { *(Forth.sp - 1) &= *Forth.sp; }, discard>),
Word("xor", WordWrap<[] { *(sp() - 1) ^= *sp(); }, discard>), Word("xor", WordWrap<[] { *(Forth.sp - 1) ^= *Forth.sp; }, discard>),
Word("lshift", WordWrap<[] { *(sp() - 1) <<= *sp(); }, discard>), Word("lshift", WordWrap<[] { *(Forth.sp - 1) <<= *Forth.sp; }, discard>),
Word("rshift", WordWrap<[] { *(sp() - 1) >>= *sp(); }, discard>), Word("rshift", WordWrap<[] { *(Forth.sp - 1) >>= *Forth.sp; }, discard>),
Word(">r", WordWrap<[] { rpush(pop()); }>), Word(">r", WordWrap<[] { Forth.rpush(Forth.pop()); }>),
Word("r>", WordWrap<[] { push(rpop()); }>), Word("r>", WordWrap<[] { Forth.push(Forth.rpop()); }>),
Word("immediate", WordWrap<[] { ((Word *)LATEST)->markImmediate(); }>), Word("immediate", WordWrap<[] { Forth.latest->markImmediate(); }>),
Word("aligned", WordWrap<[] { *sp() = aligned(*sp()); }>), Word("aligned", WordWrap<[] { *Forth.sp = aligned(*Forth.sp); }>),
Word("align", WordWrap<align>), Word("align", WordWrap<align>),
Word("literal", WordWrap<[] { if (STATE) compileliteral(); }>).markImmediate(), Word("literal", WordWrap<[] { if (Forth.state) compileliteral(); }>).markImmediate(),
Word("\'", WordWrap<tick>), Word("\'", WordWrap<tick>),
Word("_i", WordWrap<[] { *sp() = ((Word *)*sp())->immediate(); }, tobool>), Word("_i", WordWrap<[] { *Forth.sp = ((Word *)*Forth.sp)->immediate(); }, tobool>),
Word("[']", WordWrap<tick, compileliteral>).markImmediate(), Word("[']", WordWrap<tick, compileliteral>).markImmediate(),
Word("compile,", WordWrap<peek, commaSP>), Word("compile,", WordWrap<peek, commaSP>),
Word("word", WordWrap<word>), Word("word", WordWrap<word>),
Word("_b", WordWrap<[] { Word("_b", WordWrap<[] {
std::putchar('#'); // Gives a good breakpoint spot for gdb std::putchar('#'); // Gives a good breakpoint spot for gdb
}>), }>),
Word(".", WordWrap<[] { std::cout << pop() << ' '; }>) Word(".", WordWrap<[] { std::cout << Forth.pop() << ' '; }>)
); };
void getinput() void getinput()
{ {
@ -100,13 +100,13 @@ void getinput()
int main() int main()
{ {
initialize(words); std::for_each(words.begin(), words.end(), [](Word& w) { Forth.add(w); });
while (std::cin.good()) { while (std::cin.good()) {
auto result = parse(); auto result = parse();
if (result == Error::none) if (result == Error::none)
std::cout << (STATE ? "compiled" : "ok") << std::endl; std::cout << (Forth.state ? "compiled" : "ok") << std::endl;
else else
std::cout << "error " << static_cast<int>(result) << std::endl; std::cout << "error " << static_cast<int>(result) << std::endl;
} }

Loading…
Cancel
Save