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 ;
: max 2dup <= if swap drop else drop then ;
5 spaces

@ -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;
}

@ -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

@ -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();

@ -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();

@ -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);
}

@ -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

@ -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] = {

Loading…
Cancel
Save