--- /dev/null
+@tags onward forth programming-language language language-design
+# Onward Redesign
+````
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+/* TODO
+
+ * Find should ignore hidden words
+
+*/
+
+typedef intptr_t value_t;
+
+typedef struct {
+ value_t memb;
+ value_t meme;
+ value_t asb;
+ value_t asp;
+ value_t rsb;
+ value_t rsp;
+ value_t pc;
+ value_t state;
+ value_t dict;
+ value_t here;
+ char name[64];
+ value_t mem[];
+} state_t;
+
+typedef struct {
+ char* name;
+ void (*func)(state_t*);
+} builtin_t;
+
+typedef struct dict_t {
+ value_t prevd;
+ value_t name;
+ value_t firstw;
+} dict_t;
+
+typedef struct word_t {
+ value_t prevw;
+ value_t flags;
+ value_t name;
+ value_t code[];
+} word_t;
+
+enum {
+ F_BUILTIN = (1 << 0),
+ F_IMMEDIATE = (1 << 1),
+ F_HIDDEN = (1 << 2),
+};
+
+static void onward_error(state_t* os, bool cond, char* msg)
+{
+ (void)os;
+ if (cond)
+ {
+ fprintf(stderr, "error: %s\n", msg);
+ exit(1);
+ }
+}
+
+static void onward_aspush(state_t* os, value_t val) {
+ onward_error(os, (os->asp >= os->rsb), "argument stack overflow");
+ *((value_t*)((char*)os + os->asp)) = val;
+ os->asp += sizeof(value_t);
+}
+
+static value_t onward_aspop(state_t* os) {
+ onward_error(os, (os->asp <= os->asb), "argument stack underflow");
+ os->asp -= sizeof(value_t);
+ value_t val = *((value_t*)((char*)os + os->asp));
+ return val;
+}
+
+static value_t onward_aspeek(state_t* os) {
+ onward_error(os, (os->asp <= os->asb), "argument stack underflow");
+ value_t asp = (os->asp - sizeof(value_t));
+ return *((value_t*)((char*)os + asp));
+}
+
+static void onward_rspush(state_t* os, value_t val) {
+ onward_error(os, (os->rsp >= os->memb), "return stack overflow");
+ *((value_t*)((char*)os + os->rsp)) = val;
+ os->rsp += sizeof(value_t);
+}
+
+static value_t onward_rspop(state_t* os) {
+ onward_error(os, (os->rsp <= os->rsb), "return stack underflow");
+ os->rsp -= sizeof(value_t);
+ value_t val = *((value_t*)((char*)os + os->rsp));
+ return val;
+}
+
+static void* onward_reserve(state_t* os, size_t nbytes) {
+ value_t aligned = (nbytes / sizeof(value_t));
+ if (nbytes % sizeof(value_t))
+ {
+ aligned += 1;
+ }
+ void* ptr = ((char*)os + os->here);
+ os->here += (aligned * sizeof(value_t));
+ onward_error(os, (os->here >= os->meme), "memory region exhausted");
+ return ptr;
+}
+
+static void* onward_getptr(state_t* os, value_t off)
+{
+ void* ptr = NULL;
+ if (off > 0)
+ {
+ onward_error(os, (off >= os->meme), "bad memory address");
+ ptr = (char*)os + off;
+ }
+ return ptr;
+}
+
+static value_t onward_getoff(state_t* os, void* ptr)
+{
+ value_t off = (char*)ptr - (char*)os;
+ onward_error(os, (off >= os->meme), "bad memory address");
+ return off;
+}
+
+static value_t onward_memcpy(state_t* os, void* data, size_t ndata)
+{
+ void* mem = onward_reserve(os, ndata);
+ memcpy(mem, data, ndata);
+ return onward_getoff(os, mem);
+}
+
+/****************************************/
+
+value_t system_key(state_t* os);
+void system_emit(state_t* os, value_t key);
+
+static void onward_key(state_t* os) {
+ onward_aspush(os, system_key(os));
+}
+
+static void onward_emit(state_t* os) {
+ system_emit(os, onward_aspop(os));
+}
+
+static void onward_dropline(state_t* os) { // remove?
+ value_t curr;
+ do {
+ curr = system_key(os);
+ } while(((char)curr != '\n') && (curr != EOF));
+}
+
+static void onward_word(state_t* os) {
+ char* str = os->name;
+ char* end = os->name + sizeof(os->name) - 1;
+ value_t curr;
+ /* Skip any whitespace */
+ do
+ {
+ curr = system_key(os);
+ }
+ while (isspace(curr));
+ /* Copy characters into the buffer */
+ while(str < end && ((int)curr != EOF) && !isspace(curr))
+ {
+ *str++ = curr;
+ curr = system_key(os);
+ }
+ /* Terminate the string */
+ *str = '\0';
+ /* Return the internal buffer */
+ onward_aspush(os, (value_t)(os->name - (char*)os));
+}
+
+static void onward_num(state_t* os) {
+ value_t word_addr = onward_aspop(os);
+ char* word = ((char*)os + word_addr);
+ char* start = word;
+
+ /* TODO: Range check the word pointer here */
+
+ value_t success = 0;
+ value_t value = 0;
+ int sign = 1;
+ int base = 10;
+ char c;
+
+ /* Detect the sign of the number */
+ if (*start == '-') {
+ sign = -1;
+ start++;
+ }
+
+ /* Detect the base of the number to parse */
+ if (*start == '0') {
+ start++;
+ switch (*(start++)) {
+ case 'b': base = 2; break;
+ case 'o': base = 8; break;
+ case 'd': base = 10; break;
+ case 'x': base = 16; break;
+ case '\0':
+ success = 1;
+ base = -1;
+ break;
+ default: base = -1; break;
+ }
+ }
+
+ /* Parse the number */
+ if (base > 1) {
+ for (c = *start++; c != '\0'; c= *start++) {
+ /* Get the digit value */
+ if ((c >= '0') && (c <= '9'))
+ c -= '0';
+ else if (((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')))
+ c -= (c >= 'A' && c <= 'Z') ? 'A' - 10 : 'a' - 10;
+ else
+ break;
+ /* Bail if the digit value is too high */
+ if (c >= base) break;
+ /* Update the accumulated value */
+ value = (value * base) + c;
+ success = 1;
+ }
+
+ /* Convert to the required sign */
+ value *= sign;
+ }
+
+ /* Push the results back on the stack */
+ success = (success && (*(start-1) == '\0'));
+ if (success)
+ onward_aspush(os, value);
+ else
+ onward_aspush(os, (value_t)word_addr);
+ onward_aspush(os, success);
+}
+
+static void onward_find(state_t* os) {
+ char* sname = onward_getptr(os, onward_aspop(os));
+ dict_t* dict = onward_getptr(os, os->dict);
+ while (dict)
+ {
+ word_t* word = onward_getptr(os, dict->firstw);
+ while (word)
+ {
+ char* name = onward_getptr(os, word->name);
+ if (!strcmp(sname, name))
+ {
+ onward_aspush(os, onward_getoff(os, word));
+ onward_aspush(os, 1);
+ return;
+ }
+ word = onward_getptr(os, word->prevw);
+ }
+ dict = onward_getptr(os, dict->prevd);
+ }
+ onward_aspush(os, 0);
+}
+
+static void onward_exec(state_t* os) {
+ word_t* word = onward_getptr(os, onward_aspop(os));
+// onward_rspush(os->pc);
+ if (word->flags & F_BUILTIN)
+ {
+ ((void (*)(state_t*))word->code[0])(os);
+ }
+// onward_rspush(os->pc);
+}
+
+static void onward_lit(state_t* os) {
+ (void)os;
+}
+
+static void onward_create(state_t* os) {
+ char* wname = onward_getptr(os, onward_aspop(os));
+ value_t name = onward_memcpy(os, wname, strlen(wname)+1);
+ word_t* word = onward_reserve(os, sizeof(word_t));
+ (void)onward_reserve(os, sizeof(value_t));
+ dict_t* dict = onward_getptr(os, os->dict);
+ word->prevw = dict->firstw;
+ word->flags = F_HIDDEN;
+ word->name = name;
+ word->code[0] = 0;
+ dict->firstw = onward_getoff(os, word);
+}
+
+static void onward_comma(state_t* os) {
+ onward_aspush(os, *((value_t*)onward_getptr(os, os->pc)));
+}
+
+static void onward_lbrack(state_t* os) {
+ os->state = 1;
+}
+
+static void onward_rbrack(state_t* os) {
+ os->state = 0;
+}
+
+static void onward_colon(state_t* os) {
+ onward_word(os);
+ onward_create(os);
+ onward_lbrack(os);
+}
+
+static void onward_semicolon(state_t* os) {
+ dict_t* dict = onward_getptr(os, os->dict);
+ word_t* word = onward_getptr(os, dict->firstw);
+ word->flags &= ~F_HIDDEN;
+}
+
+static void onward_tick(state_t* os) {
+ onward_aspush(os, *((value_t*)onward_getptr(os, os->pc)));
+}
+
+static void onward_branch(state_t* os) {
+ os->pc += *((value_t*)onward_getptr(os, os->pc));
+}
+
+static void onward_0branch(state_t* os) {
+ if (!onward_aspop(os))
+ {
+ onward_branch(os);
+ }
+ else
+ {
+ os->pc += sizeof(value_t);
+ }
+}
+
+static void onward_interp(state_t* os) {
+ onward_word(os);
+ char* word = onward_getptr(os, onward_aspeek(os));
+ if (*word)
+ {
+ onward_find(os);
+ word_t* word = NULL;
+ if (onward_aspop(os))
+ {
+ word = onward_getptr(os, onward_aspop(os));
+ }
+ else
+ {
+ onward_num(os);
+ }
+
+ /* if compilation mode active*/
+ if ((os->state == 1) && (!word || ((word->flags & F_IMMEDIATE) == 0)) )
+ {
+ if (!word)
+ {
+ /* compile lit */
+ }
+ /* compile it */
+ onward_comma(os);
+ }
+ /* else execute it */
+ else if (word)
+ {
+ onward_exec(os);
+ }
+ }
+}
+
+static builtin_t Core_Dict[] = {
+ { "key", onward_key },
+ { "emit", onward_emit },
+ { "\\", onward_dropline },
+ { "word", onward_word },
+ { "num", onward_num },
+ { "find", onward_find },
+ { "exec", onward_exec },
+ { "lit", onward_lit },
+ { "create", onward_create },
+ { ",", onward_comma },
+ { "[", onward_lbrack },
+ { "]", onward_rbrack },
+ { ":", onward_colon },
+ { ";", onward_semicolon },
+ { "'", onward_tick },
+ { "br", onward_branch },
+ { "0br", onward_0branch },
+ { "interp", onward_interp },
+ {0,0}
+};
+
+/****************************************/
+
+static void onward_adddict(state_t* os, char* name, builtin_t* p_builtins)
+{
+ value_t dictname = onward_memcpy(os, name, strlen(name)+1);
+ dict_t* dict = onward_reserve(os, sizeof(dict_t));
+ dict->prevd = 0;
+ dict->name = dictname;
+ dict->firstw = 0;
+ for (; p_builtins->name; p_builtins++)
+ {
+ value_t name = onward_memcpy(os, p_builtins->name, strlen(p_builtins->name)+1);
+ word_t* word = onward_reserve(os, sizeof(word_t));
+ (void)onward_reserve(os, sizeof(value_t));
+ word->prevw = dict->firstw;
+ word->flags = F_BUILTIN;
+ word->name = name;
+ word->code[0] = (value_t)p_builtins->func;
+ dict->firstw = onward_getoff(os, word);
+ }
+ os->dict = onward_getoff(os, dict);
+}
+
+static state_t* onward_init(void* mem, size_t nmem, size_t nstack)
+{
+ assert((sizeof(state_t) + nstack * 2) < nmem);
+ state_t* os = mem;
+ os->meme = nmem;
+ os->memb = (sizeof(state_t) + nstack * 2);
+ os->asb = sizeof(state_t);
+ os->asp = os->asb;
+ os->rsb = os->asb + nstack;
+ os->rsp = os->rsb;
+ os->pc = 0;
+ os->state = 0;
+ os->dict = 0;
+ os->here = os->memb;
+
+ /* key emit dropline word num find exec create */
+ onward_adddict(os, "core", Core_Dict);
+ return os;
+}
+
+/****************************************/
+
+#define INCLUDE_DEFS
+#include <atf.h>
+
+static char Memory[1024*1024];
+static char* Input = "";
+static value_t Output = 0;
+
+value_t system_key(state_t* os)
+{
+ (void)os;
+ return ((Input && *Input) ? *(Input++) : EOF);
+}
+
+void system_emit(state_t* os, value_t key)
+{
+ (void)os;
+ Output = key;
+}
+
+TEST_SUITE(MachineOperations)
+{
+ TEST(onward_init should initialize block of memory for VM usage)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ CHECK(os->memb == (sizeof(state_t) + 4096));
+ CHECK(os->meme == sizeof(Memory));
+ CHECK(os->asb == sizeof(state_t));
+ CHECK(os->asp == os->asb);
+ CHECK(os->rsb == (os->asb + 2048));
+ CHECK(os->rsp == os->rsb);
+ CHECK(os->here >= os->memb);
+ CHECK(os->state == 0);
+ CHECK(os->pc == 0);
+ }
+
+ TEST(pushing and popping the argument stack)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ onward_aspush(os, 42);
+ CHECK(os->asp == (os->asb + (value_t)sizeof(value_t)));
+ value_t val1 = onward_aspeek(os);
+ CHECK(os->asp == (os->asb + (value_t)sizeof(value_t)));
+ CHECK(val1 == 42);
+ value_t val2 = onward_aspop(os);
+ CHECK(os->asp == os->asb);
+ CHECK(val2 == 42);
+ }
+
+ TEST(pushing and popping the return stack)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ onward_rspush(os, 42);
+ CHECK(os->rsp == (os->rsb + (value_t)sizeof(value_t)));
+ value_t val2 = onward_rspop(os);
+ CHECK(os->rsp == os->rsb);
+ CHECK(val2 == 42);
+ }
+
+ TEST(getting input from system)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ Input = "ABC";
+ onward_key(os);
+ CHECK('A' == onward_aspop(os));
+ onward_key(os);
+ CHECK('B' == onward_aspop(os));
+ onward_key(os);
+ CHECK('C' == onward_aspop(os));
+ onward_key(os);
+ CHECK(EOF == onward_aspop(os));
+ }
+
+ TEST(getting output to system)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ onward_aspush(os, 'A');
+ onward_emit(os);
+ CHECK(Output == 'A');
+ CHECK(os->asp == os->asb);
+ }
+
+ TEST(dropping lines from input)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ Input = "\\ this is a line that will be dropped\nA";
+ onward_dropline(os);
+ CHECK('A' == *Input);
+ CHECK(os->asp == os->asb);
+ Input = "\\ this is a line that will be dropped";
+ onward_dropline(os);
+ CHECK(os->asp == os->asb);
+ }
+
+ TEST(reading a word from input)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ Input = "abcdef";
+ onward_word(os);
+ value_t off = onward_aspop(os);
+ CHECK(off == (10 * sizeof(value_t)));
+ CHECK(!strcmp("abcdef", ((char*)os + off)));
+
+ Input = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
+ onward_word(os);
+ off = onward_aspop(os);
+ CHECK(off == (10 * sizeof(value_t)));
+ CHECK(!strcmp(
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ((char*)os + off)));
+ }
+
+ TEST(reading a number from input)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ Input = "0";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(0 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "123";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(123 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-123";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-123 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-0x2a";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-0x2a";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "0x2a";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-0d42";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "0d42";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-0o52";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "0o52";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "-0b101010";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(-42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+
+ Input = "0b101010";
+ onward_word(os);
+ onward_num(os);
+ CHECK(1 == onward_aspop(os));
+ CHECK(42 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+ }
+
+ TEST(finding a word in the dictionary)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ Input = "emit";
+ onward_word(os);
+ onward_find(os);
+ CHECK(1 == onward_aspop(os));
+ word_t* word = onward_getptr(os, onward_aspop(os));
+ CHECK(!strcmp(onward_getptr(os, word->name), "emit"));
+ CHECK(os->asb == os->asp);
+
+ Input = "foo";
+ onward_word(os);
+ onward_find(os);
+ CHECK(0 == onward_aspop(os));
+ CHECK(os->asb == os->asp);
+ }
+
+ TEST(executing word definitions)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ onward_aspush(os, 'A');
+ Input = "emit";
+ onward_word(os);
+ onward_find(os);
+ onward_exec(os);
+ CHECK(Output == 'A');
+ }
+
+ TEST(creating word definitions)
+ {
+ state_t* os = onward_init(Memory, sizeof(Memory), 2048);
+ dict_t* dict = onward_getptr(os, os->dict);
+ value_t prevw = dict->firstw;
+ Input = "foo";
+ onward_word(os);
+ onward_create(os);
+ CHECK(prevw != dict->firstw);
+ word_t* word = onward_getptr(os, dict->firstw);
+ CHECK(word->prevw == prevw);
+ CHECK(word->flags == F_HIDDEN);
+ CHECK(word->code[0] == 0);
+ CHECK(!strcmp(onward_getptr(os, word->name), "foo"));
+ }
+}
+
+// { "lit", onward_lit },
+// { ",", onward_comma },
+// { "[", onward_lbrack },
+// { "]", onward_rbrack },
+// { ":", onward_colon },
+// { ";", onward_semicolon },
+// { "'", onward_tick },
+// { "br", onward_branch },
+// { "0br", onward_0branch },
+// { "interp", onward_interp },
+
+
+int main(int argc, char** argv)
+{
+ atf_init(argc, argv);
+ RUN_EXTERN_TEST_SUITE(MachineOperations);
+ return atf_print_results();
+}
+````