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(HERE); @@ -98,6 +103,8 @@ void word() k = key(); } while (!isspace(k)); addkey(k); + + // Add a null terminator. ptr = reinterpret_cast(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(*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 DICT; +std::array 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 DICT; +/** + * Memory chunk used to store the dictoinary and stacks. + */ +extern std::array 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 #include -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 struct WordSet { std::array 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 WordWrap = [] { constexpr static Func list[1] = {