aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2023-11-24 17:25:32 -0500
committerClyne Sullivan <clyne@bitgloo.com>2023-11-24 17:25:32 -0500
commit6e6b6841ec509549dcb5ed621342cbf21ae1ec94 (patch)
treec610614b97c8b199e09b596a4190c343e3dfc8d5
parent092002a49f6a4a59200eb674cfad6657890d6ce3 (diff)
documentation and a little cleanup
-rw-r--r--core.fth2
-rw-r--r--source/core.cpp31
-rw-r--r--source/core.hpp36
-rw-r--r--source/parse.cpp5
-rw-r--r--source/parse.hpp5
-rw-r--r--source/state.cpp43
-rw-r--r--source/state.hpp42
-rw-r--r--source/types.hpp43
8 files changed, 159 insertions, 48 deletions
diff --git a/core.fth b/core.fth
index d267d08..a7336ca 100644
--- a/core.fth
+++ b/core.fth
@@ -93,3 +93,5 @@
: min 2dup <= if drop else swap drop then ;
: max 2dup <= if swap drop else drop then ;
+5 spaces
+
diff --git a/source/core.cpp b/source/core.cpp
index 7daf1ae..c97203e 100644
--- a/source/core.cpp
+++ b/source/core.cpp
@@ -22,9 +22,11 @@
void jump(FuncList ip)
{
+ // IP is incremented before its next execution.
IP = ip - 1;
}
+// LITERAL's run-time semantics: push the given value onto the stack.
static auto literall = WordWrap<[] {
*++SP = (Cell)*++IP;
}>();
@@ -51,6 +53,7 @@ void addkey(int k)
int key()
{
+ // Block until input is available.
while (!haskey())
getinput();
@@ -81,11 +84,13 @@ void align()
void word()
{
+ // Skip leading whitespace.
int k;
do {
k = key();
} while (isspace(k));
+ // Collect the word's text.
char *ptr;
do {
ptr = reinterpret_cast<char *>(HERE);
@@ -98,6 +103,8 @@ void word()
k = key();
} while (!isspace(k));
addkey(k);
+
+ // Add a null terminator.
ptr = reinterpret_cast<char *>(HERE);
*ptr = '\0';
++HERE;
@@ -105,47 +112,61 @@ void word()
void colon()
{
+ // Collect (and store) the word's name.
align();
auto name = HERE;
word();
align();
+ // Build the Word structure.
comma(HERE + 4 * sizeof(Cell)); // exec ptr
- comma(name); // name ptr
- *++SP = (Cell)comma(0); // link (filled by latest)
- comma(0); // immediate
-
+ comma(name); // name ptr
+ *++SP = (Cell)comma(0); // link (to be set by semic())
+ comma(0); // immediate
+
+ // The word's execution begins with a prologue that technically performs
+ // the "call" to this word.
+ // By including this in the word's definition, execution can avoid caring
+ // about if it is running words or routines (i.e. pre-defined words).
comma((Cell)+[](FuncList *ip) {
++ip;
*++RP = (Cell)IP;
jump((FuncList)*ip);
});
+ // The actual function list will begin one Cell beyond here.
comma(HERE + sizeof(Cell));
+ // Enter compiling state.
STATE = -1;
}
void semic()
{
+ // Add exit routine.
comma((Cell)fexit);
+ // Complete the new word's linkage to make it usable.
auto link = (Cell *)*SP--;
*link = LATEST;
LATEST = (Cell)(link - 2);
+ // Exit compilation state.
STATE = 0;
}
-// : ' bl word find drop ;
+// TODO define in Forth? ": ' bl word find drop ;"
void tick()
{
+ // Get the name to look up.
auto name = (char *)HERE;
word();
+ // Look up the name and push the result.
int len = HERE - (Cell)name - 1;
auto word = find(name, len);
*++SP = (Cell)word;
+ // Deallocate `name`.
HERE = (Cell)name;
}
diff --git a/source/core.hpp b/source/core.hpp
index a134804..d85cccc 100644
--- a/source/core.hpp
+++ b/source/core.hpp
@@ -20,29 +20,35 @@
#include "types.hpp"
+/**
+ * To be implemented by the user: Adds available input to the source buffer.
+ * @see addkey(int k)
+ */
extern void getinput();
+/**
+ * "Function exit" word, analagous to a function's return statement.
+ */
constexpr auto fexit = WordWrap<[] {
extern FuncList IP;
extern Cell *RP;
IP = reinterpret_cast<FuncList>(*RP--);
}>();
-void jump(FuncList ip);
-void jumper();
-void compileliteral();
-
-bool haskey();
-void addkey(int k);
-
-int key();
-Cell *comma(Cell n);
-Addr aligned(Addr addr);
-void align();
-void word();
-void colon();
-void semic();
-void tick();
+void jump(FuncList ip); /** Jumps to the given instruction pointer. */
+void compileliteral(); /** Compiles LITERAL into the current definition. */
+
+bool haskey(); /** Returns true if the source buffer is not empty. */
+void addkey(int k); /** Adds the given character to the source buffer. */
+int key(); /** Gets the next key available from the source buffer. */
+
+Cell *comma(Cell n); /** Stores `n` to HERE++, returns `n`'s storage. */
+Addr aligned(Addr addr); /** Aligns the given address and returns it. */
+void align(); /** Aligns HERE to the next Cell boundary. */
+void word(); /** Gets word from source buffer, stores and allots from HERE. */
+void colon(); /** Begins definition of a new word. */
+void semic(); /** Ends the current word definition which becomes new LATEST. */
+void tick(); /** Gets the execution token for the source buffer's next word. */
#endif // CORE_HPP
diff --git a/source/parse.cpp b/source/parse.cpp
index 012d708..74f7e6a 100644
--- a/source/parse.cpp
+++ b/source/parse.cpp
@@ -45,22 +45,27 @@ void parseSource()
auto start = (char *)DICT[DIdxSource];
auto end = start;
+ // Try to build words while we have input available.
while (haskey()) {
end = (char *)++DICT[DIdxSource];
--DICT[DIdxSrcLen];
if (isspace(*end)) {
+ // May have collected a word.
+ // parseword() will check if start != end.
parseword(start, end);
start = (char *)(DICT[DIdxSource] + 1);
}
}
+ // Parse the final word if it is non-empty.
if (start != end)
parseword(start, end);
}
void parse()
{
+ // Reset source buffer and try to fill it with getinput().
DICT[DIdxSource] = (Cell)&DICT[DIdxBegin];
DICT[DIdxSrcLen] = 0;
getinput();
diff --git a/source/parse.hpp b/source/parse.hpp
index 7bf0c27..a6816f3 100644
--- a/source/parse.hpp
+++ b/source/parse.hpp
@@ -15,4 +15,9 @@
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+/***
+ * Resets the source buffer, calls getinput() to fill the source buffer, then
+ * parses the source buffer's contents.
+ */
void parse();
+
diff --git a/source/state.cpp b/source/state.cpp
index 27d624c..a2f424c 100644
--- a/source/state.cpp
+++ b/source/state.cpp
@@ -26,7 +26,7 @@ Cell *SP = DICT.data() + DICT.size() - DS;
Cell *RP = DICT.data() + DICT.size() - DS - RS;
FuncList IP = nullptr;
-std::array<Cell, 2048> DICT;
+std::array<Cell, DictSize> DICT;
Cell& HERE = DICT[DIdxHere];
Cell& LATEST = DICT[DIdxLatest];
@@ -34,19 +34,39 @@ Cell& STATE = DICT[DIdxState];
void executor(FuncList *list)
{
+ // TODO Use setjmp to recover from errors?
/*if (setjmp(jmpbuf) == 0)*/ {
- // Execute the first bit of "word".
- // If it is a "WordWrap", it will exit without changing IP.
- // If it is a defined word, IP will be set to the word's body.
- auto ip = (FuncList)list;
- auto po = (void (**)(FuncList))*ip;
- // Pass in po's location so the call can fetch beyond itself.
- (*po)((FuncList)*ip);
+ FuncList body;
+ void (*entry)(FuncList);
+
+ // 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) {
- ++IP;
- auto po = (void (**)(FuncList))*IP;
- (*po)((FuncList)*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.
+ entry = (void (*)(FuncList))*body;
+ entry(body);
}
//std::longjmp(jmpbuf, 1);
@@ -55,6 +75,7 @@ void executor(FuncList *list)
void execute1(Word *word)
{
+ // IP must initially be zero if executing a word at the top level.
IP = 0;
executor(&word->list);
}
diff --git a/source/state.hpp b/source/state.hpp
index 91ce12f..2a4668e 100644
--- a/source/state.hpp
+++ b/source/state.hpp
@@ -20,8 +20,9 @@
#include "types.hpp"
-constexpr Addr DS = 16;
-constexpr Addr RS = 16;
+constexpr Addr DS = 16; /** Data stack size */
+constexpr Addr RS = 16; /** Return stack size */
+constexpr auto DictSize = 2048u; /** Dictionary size */
constexpr Addr DIdxBase = 0;
constexpr Addr DIdxHere = 1;
@@ -32,16 +33,23 @@ constexpr Addr DIdxSrcLen = 5;
constexpr Addr DIdxInBuf = 6;
constexpr Addr DIdxBegin = DIdxInBuf + 80 * sizeof(char);
-extern std::array<Cell, 2048> DICT;
+/**
+ * Memory chunk used to store the dictoinary and stacks.
+ */
+extern std::array<Cell, DictSize> DICT;
-extern Cell& HERE;
-extern Cell& LATEST;
-extern Cell& STATE;
+extern Cell& HERE; /** Linked to HERE's storage in DICT. */
+extern Cell& LATEST; /** Linked to LATEST's storage in DICT. */
+extern Cell& STATE; /** Linked to STATE's storage in DICT. */
-extern Cell *SP;
-extern Cell *RP;
-extern FuncList IP;
+extern Cell *SP; /** Data stack pointer */
+extern Cell *RP; /** Return stack pointer */
+extern FuncList IP; /** Instruction pointer */
+/**
+ * Initializes the dictionary to default values.
+ * @param wordset The initial WordSet of pre-defined words.
+ */
inline void initialize(const auto& wordset)
{
LATEST = (Cell)wordset.latest;
@@ -49,8 +57,24 @@ inline void initialize(const auto& wordset)
STATE = 0;
}
+/**
+ * Begins execution with the given function pointer list.
+ * @param list Function pointer list to execute
+ */
void executor(FuncList *list);
+
+/**
+ * Executes the given word by calling executor on its definition.
+ * @param word The word to execute
+ */
void execute1(Word *word);
+
+/**
+ * Looks up the definition of the given word.
+ * @param s The name of the word to find
+ * @param len The character count of the word's name
+ * @return Pointer to the word's definition or nullptr if not found
+ */
Word *find(const char *s, int len);
#endif // STATE_HPP
diff --git a/source/types.hpp b/source/types.hpp
index 03e9f2a..461d835 100644
--- a/source/types.hpp
+++ b/source/types.hpp
@@ -22,19 +22,29 @@
#include <cstddef>
#include <cstdint>
-using Cell = intptr_t;
-using Addr = uintptr_t;
-using Func = void (*)();
-using FuncList = Func const *;
+using Cell = intptr_t; /** Type of a dictionary's cell. */
+using Addr = uintptr_t; /** Type of a dictionary address. */
+using Func = void (*)(); /** Words are built of these types of functions. */
+using FuncList = Func const *; /** Words are defined as a list of Func. */
+
+// Cells, Addrs, and Funcs must all be the same size in bytes since the
+// dictionary stores all of these types of values.
static_assert(sizeof(Cell) == sizeof(Addr));
static_assert(sizeof(Cell) == sizeof(Func));
+/**
+ * @struct Word
+ * @brief Structure of a word's definition.
+ * A word has a name, a body (list of functions), a link to the next word
+ * definition, and an immediate indicator. This structure is matched when
+ * words are defined in the dictionary by ":" and ";".
+ */
struct Word {
- FuncList list;
- const char *name;
- Word *link = nullptr;
- Cell imm = 0;
+ FuncList list; /** The word's body/definition. */
+ const char *name; /** Null-terminated string of word's name. */
+ Word *link = nullptr; /** Link to next word in definition chain. */
+ Cell imm = 0; /** If non-zero, word is "immediate". */
constexpr Word(const char *n, FuncList l):
list(l), name(n) {}
@@ -49,18 +59,31 @@ struct Word {
}
};
+// Words are built in the dictionary by the user using ":". These assertions
+// ensure that the structures created by ":" match the actual structure of a
+// Word exactly.
+
static_assert(offsetof(Word, list) == 0);
static_assert(offsetof(Word, name) == 1 * sizeof(Cell));
static_assert(offsetof(Word, link) == 2 * sizeof(Cell));
static_assert(offsetof(Word, imm) == 3 * 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...}
{
@@ -72,6 +95,10 @@ struct WordSet
}
};
+/**
+ * Wraps the given functions so that a function pointer list can be created
+ * and used for a word's definition.
+ */
template<auto... funcs>
auto WordWrap = [] {
constexpr static Func list[1] = {