documentation and a little cleanup

main
Clyne 10 months ago
parent 092002a49f
commit 6e6b6841ec
Signed by: clyne
GPG Key ID: 3267C8EBF3F9AFC7

@ -93,3 +93,5 @@
: min 2dup <= if drop else swap drop then ; : min 2dup <= if drop else swap drop then ;
: max 2dup <= if swap drop else drop then ; : max 2dup <= if swap drop else drop then ;
5 spaces

@ -22,9 +22,11 @@
void jump(FuncList ip) void jump(FuncList ip)
{ {
// IP is incremented before its next execution.
IP = ip - 1; IP = ip - 1;
} }
// LITERAL's run-time semantics: push the given value onto the stack.
static auto literall = WordWrap<[] { static auto literall = WordWrap<[] {
*++SP = (Cell)*++IP; *++SP = (Cell)*++IP;
}>(); }>();
@ -51,6 +53,7 @@ void addkey(int k)
int key() int key()
{ {
// Block until input is available.
while (!haskey()) while (!haskey())
getinput(); getinput();
@ -81,11 +84,13 @@ void align()
void word() void word()
{ {
// Skip leading whitespace.
int k; int k;
do { do {
k = key(); k = key();
} while (isspace(k)); } while (isspace(k));
// Collect the word's text.
char *ptr; char *ptr;
do { do {
ptr = reinterpret_cast<char *>(HERE); ptr = reinterpret_cast<char *>(HERE);
@ -98,6 +103,8 @@ void word()
k = key(); k = key();
} while (!isspace(k)); } while (!isspace(k));
addkey(k); addkey(k);
// Add a null terminator.
ptr = reinterpret_cast<char *>(HERE); ptr = reinterpret_cast<char *>(HERE);
*ptr = '\0'; *ptr = '\0';
++HERE; ++HERE;
@ -105,47 +112,61 @@ void word()
void colon() void colon()
{ {
// Collect (and store) the word's name.
align(); align();
auto name = HERE; auto name = HERE;
word(); word();
align(); align();
// Build the Word structure.
comma(HERE + 4 * sizeof(Cell)); // exec ptr comma(HERE + 4 * sizeof(Cell)); // exec ptr
comma(name); // name ptr comma(name); // name ptr
*++SP = (Cell)comma(0); // link (filled by latest) *++SP = (Cell)comma(0); // link (to be set by semic())
comma(0); // immediate 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) { comma((Cell)+[](FuncList *ip) {
++ip; ++ip;
*++RP = (Cell)IP; *++RP = (Cell)IP;
jump((FuncList)*ip); jump((FuncList)*ip);
}); });
// The actual function list will begin one Cell beyond here.
comma(HERE + sizeof(Cell)); comma(HERE + sizeof(Cell));
// Enter compiling state.
STATE = -1; STATE = -1;
} }
void semic() void semic()
{ {
// Add exit routine.
comma((Cell)fexit); comma((Cell)fexit);
// Complete the new word's linkage to make it usable.
auto link = (Cell *)*SP--; auto link = (Cell *)*SP--;
*link = LATEST; *link = LATEST;
LATEST = (Cell)(link - 2); LATEST = (Cell)(link - 2);
// Exit compilation state.
STATE = 0; STATE = 0;
} }
// : ' bl word find drop ; // TODO define in Forth? ": ' bl word find drop ;"
void tick() void tick()
{ {
// Get the name to look up.
auto name = (char *)HERE; auto name = (char *)HERE;
word(); word();
// Look up the name and push the result.
int len = HERE - (Cell)name - 1; int len = HERE - (Cell)name - 1;
auto word = find(name, len); auto word = find(name, len);
*++SP = (Cell)word; *++SP = (Cell)word;
// Deallocate `name`.
HERE = (Cell)name; HERE = (Cell)name;
} }

@ -20,29 +20,35 @@
#include "types.hpp" #include "types.hpp"
/**
* To be implemented by the user: Adds available input to the source buffer.
* @see addkey(int k)
*/
extern void getinput(); extern void getinput();
/**
* "Function exit" word, analagous to a function's return statement.
*/
constexpr auto fexit = WordWrap<[] { constexpr auto fexit = WordWrap<[] {
extern FuncList IP; extern FuncList IP;
extern Cell *RP; extern Cell *RP;
IP = reinterpret_cast<FuncList>(*RP--); IP = reinterpret_cast<FuncList>(*RP--);
}>(); }>();
void jump(FuncList ip); void jump(FuncList ip); /** Jumps to the given instruction pointer. */
void jumper(); void compileliteral(); /** Compiles LITERAL into the current definition. */
void compileliteral();
bool haskey(); /** Returns true if the source buffer is not empty. */
bool haskey(); void addkey(int k); /** Adds the given character to the source buffer. */
void addkey(int k); int key(); /** Gets the next key available from the source buffer. */
int key(); Cell *comma(Cell n); /** Stores `n` to HERE++, returns `n`'s storage. */
Cell *comma(Cell n); Addr aligned(Addr addr); /** Aligns the given address and returns it. */
Addr aligned(Addr addr); void align(); /** Aligns HERE to the next Cell boundary. */
void align(); void word(); /** Gets word from source buffer, stores and allots from HERE. */
void word(); void colon(); /** Begins definition of a new word. */
void colon(); void semic(); /** Ends the current word definition which becomes new LATEST. */
void semic(); void tick(); /** Gets the execution token for the source buffer's next word. */
void tick();
#endif // CORE_HPP #endif // CORE_HPP

@ -45,22 +45,27 @@ void parseSource()
auto start = (char *)DICT[DIdxSource]; auto start = (char *)DICT[DIdxSource];
auto end = start; auto end = start;
// Try to build words while we have input available.
while (haskey()) { while (haskey()) {
end = (char *)++DICT[DIdxSource]; end = (char *)++DICT[DIdxSource];
--DICT[DIdxSrcLen]; --DICT[DIdxSrcLen];
if (isspace(*end)) { if (isspace(*end)) {
// May have collected a word.
// parseword() will check if start != end.
parseword(start, end); parseword(start, end);
start = (char *)(DICT[DIdxSource] + 1); start = (char *)(DICT[DIdxSource] + 1);
} }
} }
// Parse the final word if it is non-empty.
if (start != end) if (start != end)
parseword(start, end); parseword(start, end);
} }
void parse() void parse()
{ {
// Reset source buffer and try to fill it with getinput().
DICT[DIdxSource] = (Cell)&DICT[DIdxBegin]; DICT[DIdxSource] = (Cell)&DICT[DIdxBegin];
DICT[DIdxSrcLen] = 0; DICT[DIdxSrcLen] = 0;
getinput(); getinput();

@ -15,4 +15,9 @@
// along with this library; if not, write to the Free Software Foundation, Inc., // along with this library; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. // 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(); void parse();

@ -26,7 +26,7 @@ Cell *SP = DICT.data() + DICT.size() - DS;
Cell *RP = DICT.data() + DICT.size() - DS - RS; Cell *RP = DICT.data() + DICT.size() - DS - RS;
FuncList IP = nullptr; FuncList IP = nullptr;
std::array<Cell, 2048> DICT; std::array<Cell, DictSize> DICT;
Cell& HERE = DICT[DIdxHere]; Cell& HERE = DICT[DIdxHere];
Cell& LATEST = DICT[DIdxLatest]; Cell& LATEST = DICT[DIdxLatest];
@ -34,19 +34,39 @@ Cell& STATE = DICT[DIdxState];
void executor(FuncList *list) void executor(FuncList *list)
{ {
// TODO Use setjmp to recover from errors?
/*if (setjmp(jmpbuf) == 0)*/ { /*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) { while (IP) {
++IP; // Retrieve next function pointer list.
auto po = (void (**)(FuncList))*IP; body = (FuncList)*++IP;
(*po)((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); //std::longjmp(jmpbuf, 1);
@ -55,6 +75,7 @@ void executor(FuncList *list)
void execute1(Word *word) void execute1(Word *word)
{ {
// IP must initially be zero if executing a word at the top level.
IP = 0; IP = 0;
executor(&word->list); executor(&word->list);
} }

@ -20,8 +20,9 @@
#include "types.hpp" #include "types.hpp"
constexpr Addr DS = 16; constexpr Addr DS = 16; /** Data stack size */
constexpr Addr RS = 16; constexpr Addr RS = 16; /** Return stack size */
constexpr auto DictSize = 2048u; /** Dictionary size */
constexpr Addr DIdxBase = 0; constexpr Addr DIdxBase = 0;
constexpr Addr DIdxHere = 1; constexpr Addr DIdxHere = 1;
@ -32,16 +33,23 @@ constexpr Addr DIdxSrcLen = 5;
constexpr Addr DIdxInBuf = 6; constexpr Addr DIdxInBuf = 6;
constexpr Addr DIdxBegin = DIdxInBuf + 80 * sizeof(char); 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& HERE; /** Linked to HERE's storage in DICT. */
extern Cell& LATEST; extern Cell& LATEST; /** Linked to LATEST's storage in DICT. */
extern Cell& STATE; extern Cell& STATE; /** Linked to STATE's storage in DICT. */
extern Cell *SP; extern Cell *SP; /** Data stack pointer */
extern Cell *RP; extern Cell *RP; /** Return stack pointer */
extern FuncList IP; 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) inline void initialize(const auto& wordset)
{ {
LATEST = (Cell)wordset.latest; LATEST = (Cell)wordset.latest;
@ -49,8 +57,24 @@ inline void initialize(const auto& wordset)
STATE = 0; STATE = 0;
} }
/**
* Begins execution with the given function pointer list.
* @param list Function pointer list to execute
*/
void executor(FuncList *list); void executor(FuncList *list);
/**
* Executes the given word by calling executor on its definition.
* @param word The word to execute
*/
void execute1(Word *word); 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); Word *find(const char *s, int len);
#endif // STATE_HPP #endif // STATE_HPP

@ -22,19 +22,29 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
using Cell = intptr_t; using Cell = intptr_t; /** Type of a dictionary's cell. */
using Addr = uintptr_t; using Addr = uintptr_t; /** Type of a dictionary address. */
using Func = void (*)(); using Func = void (*)(); /** Words are built of these types of functions. */
using FuncList = Func const *; 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(Addr));
static_assert(sizeof(Cell) == sizeof(Func)); 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 { struct Word {
FuncList list; FuncList list; /** The word's body/definition. */
const char *name; const char *name; /** Null-terminated string of word's name. */
Word *link = nullptr; Word *link = nullptr; /** Link to next word in definition chain. */
Cell imm = 0; Cell imm = 0; /** If non-zero, word is "immediate". */
constexpr Word(const char *n, FuncList l): constexpr Word(const char *n, FuncList l):
list(l), name(n) {} 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, list) == 0);
static_assert(offsetof(Word, name) == 1 * sizeof(Cell)); static_assert(offsetof(Word, name) == 1 * sizeof(Cell));
static_assert(offsetof(Word, link) == 2 * sizeof(Cell)); 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> template<typename... Words>
struct WordSet struct WordSet
{ {
std::array<Word, sizeof...(Words)> words; std::array<Word, sizeof...(Words)> words;
Word *latest; 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): constexpr WordSet(Words... ws):
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> template<auto... funcs>
auto WordWrap = [] { auto WordWrap = [] {
constexpr static Func list[1] = { constexpr static Func list[1] = {

Loading…
Cancel
Save