]> code.bitgloo.com Git - clyne/sprit-forth.git/commitdiff
documentation and a little cleanup
authorClyne Sullivan <clyne@bitgloo.com>
Fri, 24 Nov 2023 22:25:32 +0000 (17:25 -0500)
committerClyne Sullivan <clyne@bitgloo.com>
Fri, 24 Nov 2023 22:25:32 +0000 (17:25 -0500)
core.fth
source/core.cpp
source/core.hpp
source/parse.cpp
source/parse.hpp
source/state.cpp
source/state.hpp
source/types.hpp

index d267d087cdeb15995a806e15fecb973cb796c0b1..a7336cadaf5730f0952b44969cfae6806fa9a49c 100644 (file)
--- 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
+
index 7daf1ae0705467bdadbe3b54989d52bc2cada056..c97203e196114c628bb62dbf8cb538f527379a7a 100644 (file)
 
 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;
 }
 
index a13480460b1ffeac991dfd188c5ad515119fc75e..d85cccc51d5d80bf392b6a710e9d302f5f95f243 100644 (file)
 
 #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
 
index 012d70830e32f69a132f5ebcad6a53c931be73cf..74f7e6af729bdc312c47d8d8916f0842b75d417c 100644 (file)
@@ -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();
index 7bf0c2778c6d4b442ae60be526fa71b2ca4ce53d..a6816f3280859cb298ae6aa5bb685ceeb4450d7d 100644 (file)
@@ -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();
+
index 27d624c2e32219f552487b53276d173c6e5f89ea..a2f424c62456f111674a60106664f7af3e5d2234 100644 (file)
@@ -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);
 }
index 91ce12f2cbf326ac8f8032f5e9ef3565d40b5bb2..2a4668e265506e4d779ed06467b6c8e0252e6a96 100644 (file)
@@ -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
index 03e9f2add685ed39a52650fd9c9ed9c22f44a59b..461d8357fed4c52303bc19d9910577509cf66b85 100644 (file)
 #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] = {