merged dict with heap

master
Ondřej Hruška 3 years ago
parent 964aec76c8
commit c2cba4c057
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 16
      include/fh_mem.h
  2. 33
      include/fh_runtime.h
  3. 70
      src/fh_builtins.c
  4. 32
      src/fh_mem.c
  5. 75
      src/fh_runtime.c
  6. 16
      src/main.c

@ -7,6 +7,8 @@
#ifndef FORTH_FH_MEM_H
#define FORTH_FH_MEM_H
void fh_align(struct fh_thread_s *fh);
void fh_setbase(struct fh_thread_s *fh, uint32_t base);
enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst);
@ -26,16 +28,8 @@ void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint3
enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len);
void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint32_t len);
static inline char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) {
return (char *) &fh->heap[addr];
}
static inline struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) {
return (void *) &fh->heap[addr];
}
static inline struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) {
return (struct fh_word_s *) &fh->heap[addr];
}
char *fh_str_at(struct fh_thread_s *fh, uint32_t addr);
struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr);
struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr);
#endif //FORTH_FH_MEM_H

@ -94,11 +94,17 @@ enum fh_substate {
FH_SUBSTATE_MAX,
};
/** Indicates that this is a built-in instruction and not a word call */
#define WORDFLAG_BUILTIN 0x01
/** Indicates that this instruction should always be treated as interpreted */
#define WORDFLAG_IMMEDIATE 0x02
/** Word struct as they are stored in the dictionary */
struct fh_word_s {
uint32_t index;
/** Linked list pointer to previous word */
uint32_t previous;
/** Word name */
char name[MAX_NAME_LEN];
char name[MAX_NAME_LEN]; // XXX this wastes RAM!
/**
* Handler function.
* Builtin functions use pre-defined native handlers.
@ -106,17 +112,14 @@ struct fh_word_s {
* bytecode at 'start' address of the compile-memory area.
*/
word_exec_t handler;
/** Indicates that this is a built-in instruction and not a word call */
bool builtin;
/** Indicates that this instruction should always be treated as interpreted */
bool immediate;
uint32_t flags;
/** Start address in case of user words, or param for builtins */
union {
uint32_t start;
uint32_t param;
};
uint32_t param;
};
#define MAGICADDR_DICTFIRST 0xFFFFFFFFULL
#define DICTWORD_SIZE sizeof(struct fh_word_s)
/**
* Forth runtime instance - state variables and memory areas.
*
@ -124,6 +127,8 @@ struct fh_word_s {
* to a shared pointer if multi-threading and synchronization is added.
*/
struct fh_thread_s {
// TODO stacks could live on heap too and share space with some other structures
/** Data stack */
uint32_t data_stack[DATA_STACK_DEPTH];
size_t data_stack_top;
@ -140,10 +145,8 @@ struct fh_thread_s {
/** Pointer into the compile buffer for execution */
uint32_t execptr;
/** Word dict */
struct fh_word_s dict[DICT_SIZE];
uint32_t dict_top;
/** Address of the last dict word */
uint32_t dict_last;
/** Forth state */
enum fh_state state;
@ -193,6 +196,6 @@ enum fh_error fh_handle_ascii_word(
size_t wordlen
);
enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w);
enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr);
#endif //FORTH_FH_RUNTIME_H

@ -359,17 +359,19 @@ static enum fh_error w_slash_mod(struct fh_thread_s *fh, const struct fh_word_s
static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
ENSURE_STATE(FH_STATE_INTERPRET);
fh_setstate(fh, FH_STATE_COMPILE, FH_SUBSTATE_COLON_NAME);
if (fh->dict_top >= DICT_SIZE) {
return FH_ERR_DICT_FULL;
}
struct fh_word_s *new_word = &fh->dict[fh->dict_top];
new_word->index = fh->dict_top;
new_word->start = fh->here;
uint32_t ptr;
TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr));
struct fh_word_s *new_word = fh_word_at(fh, ptr);
new_word->previous = fh->dict_last;
new_word->param = fh->here;
new_word->handler = w_user_word;
fh->dict_last = ptr;
return FH_OK;
}
@ -427,24 +429,6 @@ static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s
/* Return to interpret state */
fh_setstate(fh, FH_STATE_INTERPRET, 0);
struct fh_word_s *new_word = &fh->dict[fh->dict_top];
/* Now, check if a word with this name already exists. The new one should be used. */
struct fh_word_s *old_word = &fh->dict[0];
while (old_word->handler && old_word != new_word) {
if (0 == strncasecmp(new_word->name, old_word->name, MAX_NAME_LEN)) {
// We can't move the new definition because of RECURSE already using its address.
// Instead, redirect and wipe the old name.
// Note that this leaks compile memory!
old_word->start = new_word->start;
old_word->name[0] = 0;
break;
}
old_word++;
}
fh->dict_top++;
return FH_OK;
}
@ -453,12 +437,12 @@ static enum fh_error w_immediate(struct fh_thread_s *fh, const struct fh_word_s
(void) w;
enum fh_error rv;
if (fh->dict_top == 0) {
if (fh->dict_last == 0) {
LOGE("Dict is empty, cannot modify previous word!");
return FH_ERR_INVALID_STATE;
}
fh->dict[fh->dict_top - 1].immediate = 1;
fh_word_at(fh, fh->dict_last)->flags |= WORDFLAG_IMMEDIATE;
return FH_OK;
}
@ -471,7 +455,7 @@ static enum fh_error w_recurse(struct fh_thread_s *fh, const struct fh_word_s *w
ENSURE_STATE(FH_STATE_COMPILE);
instr_init(&instr, FH_INSTR_WORD, fh->dict_top);
instr_init(&instr, FH_INSTR_WORD, fh->dict_last);
TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK;
@ -1157,6 +1141,19 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
const struct name_and_handler builtins[] = {
{"", w_error_word0, 1, 0},
/* Weird meta stuff */
{"immediate", w_immediate, 1, 0},
{"postpone", w_postpone, 1, 0},
{"[", w_leftbracket, 1, 0},
{"]", w_rightbracket, 1, 0},
{"literal", w_literal, 1, 0},
/* Runtime stats */
{"depth", w_depth, 0, 0},
{"unused", w_unused, 0, 0},
/* Debug tools & system */
{"reset", w_reset, 1, 0},
{"see", w_see, 0, 0},
{"bye", w_bye, 0, 0},
/* Strings & Chars */
{"s\"", w_s_quote, 1, 0},
{".\"", w_dot_quote, 1, 0},
@ -1263,33 +1260,20 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
{";", w_semicolon, 1, 0},
{"\\", w_backslash, 1, 0}, // line comment
{"(", w_paren, 1, 0}, // enclosed comment
/* Weird meta stuff */
{"immediate", w_immediate, 1, 0},
{"postpone", w_postpone, 1, 0},
{"[", w_leftbracket, 1, 0},
{"]", w_rightbracket, 1, 0},
{"literal", w_literal, 1, 0},
/* Runtime stats */
{"depth", w_depth, 0, 0},
{"unused", w_unused, 0, 0},
/* Debug tools & system */
{"reset", w_reset, 1, 0},
{"see", w_see, 0, 0},
{"bye", w_bye, 0, 0},
{ /* end marker */ }
};
LOG("Adding builtin words");
// foreach
struct fh_word_s w;
const struct name_and_handler *p = builtins;
enum fh_error rv;
while (p->handler) {
strcpy(w.name, p->name);
w.index = fh->dict_top;
w.handler = p->handler;
w.builtin = 1;
w.immediate = p->immediate;
w.flags = WORDFLAG_BUILTIN | (p->immediate ? WORDFLAG_IMMEDIATE : 0);
w.param = p->param;
rv = fh_add_word(&w, fh);
if (rv != FH_OK) {

@ -5,6 +5,11 @@
#include "fh_runtime.h"
#include "fh_mem.h"
void fh_align(struct fh_thread_s *fh)
{
fh->here = WORDALIGNED(fh->here);
}
void fh_setbase(struct fh_thread_s *fh, uint32_t base) {
LOG("BASE = %d", base);
fh->base = base;
@ -105,6 +110,9 @@ enum fh_error fh_heap_reserve(
*addr = p;
}
// Erase the region. This is out of abundance of caution, not really needed if it was erased initially. Maybe.
memset(&fh->heap[p], 0, len);
fh->here = WORDALIGNED(p + len);
return FH_OK;
@ -120,7 +128,7 @@ void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint3
enum fh_error fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len)
{
enum fh_error rv;
uint32_t addr;
uint32_t addr = 0;
TRY(fh_heap_reserve(fh, len, &addr));
fh_heap_write(fh, addr, src, len);
return FH_OK;
@ -131,3 +139,25 @@ void fh_heap_copy(struct fh_thread_s *fh, uint32_t addr, uint32_t srcaddr, uint3
{
memcpy(&fh->heap[addr], &fh->heap[srcaddr], len);
}
char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_SIZE) {
LOGE("fh_str_at out of bounds!");
}
return (char *) &fh->heap[addr];
}
struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_SIZE) {
LOGE("fh_instr_at out of bounds!");
}
return (void *) &fh->heap[addr];
}
struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_SIZE) {
LOGE("fh_word_at out of bounds!");
}
return (struct fh_word_s *) &fh->heap[addr];
}

@ -37,10 +37,19 @@ static const char *substatenames[FH_SUBSTATE_MAX] = {
/** Add a word to the dictionary. */
enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh)
{
if (fh->dict_top == DICT_SIZE) {
return FH_ERR_DICT_FULL;
}
memcpy(&fh->dict[fh->dict_top++], w, sizeof(struct fh_word_s));
enum fh_error rv;
fh_align(fh);
uint32_t ptr = fh->here;
TRY(fh_heap_put(fh, w, DICTWORD_SIZE));
//LOG("Added word \"%s\" at 0x%08x", w->name, ptr);
// thread it onto the linked list
fh_word_at(fh, ptr)->previous = fh->dict_last;
fh->dict_last = ptr;
return FH_OK;
}
@ -83,7 +92,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
LOG("Run user word: %s", w->name);
TRY(rs_push(fh, fh->execptr));
fh->execptr = w->start;
fh->execptr = w->param;
instr:;
if (fh->state == FH_STATE_QUIT) {
@ -108,8 +117,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
case FH_INSTR_POSTPONED_WORD:
if (fh->state == FH_STATE_COMPILE) {
w2 = &fh->dict[instr->data];
if (w2->immediate) {
w2 = fh_word_at(fh, instr->data);
if (w2->flags & WORDFLAG_IMMEDIATE) {
LOG("Call immediate postponed word: %s", w2->name);
TRY(w2->handler(fh, w2));
} else {
@ -124,8 +133,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
goto instr;
case FH_INSTR_WORD:
w2 = &fh->dict[instr->data];
if (w2->builtin) {
w2 = fh_word_at(fh, instr->data);
if (w2->flags & WORDFLAG_BUILTIN) {
LOG("Exec: builtin-word \"%s\"", w2->name);
w2->handler(fh, w2);
if (fh->substate == FH_SUBSTATE_EXIT) {
@ -139,7 +148,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
goto instr;
} else {
LOG("Exec: user-word %s (CALL)", w2->name);
w = &fh->dict[instr->data];
w = fh_word_at(fh, instr->data);
goto call;
}
@ -202,6 +211,7 @@ enum fh_error fh_init(struct fh_thread_s *fh)
/* Make sure we have a clean state */
memset(fh, 0, sizeof(struct fh_thread_s));
fh->dict_last = MAGICADDR_DICTFIRST;
TRY(register_builtin_words(fh));
fh->execptr = MAGICADDR_INTERACTIVE;
@ -248,13 +258,14 @@ static enum fh_error fh_handle_quoted_string(
return FH_OK;
}
enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w)
enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr)
{
struct fh_instruction_s instr;
enum fh_error rv;
if (fh->state == FH_STATE_COMPILE && !w->immediate) {
struct fh_word_s *w = fh_word_at(fh, addr);
if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) {
LOG("Compile word call: %s", w->name);
instr_init(&instr, FH_INSTR_WORD, w->index);
instr_init(&instr, FH_INSTR_WORD, addr);
TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
} else {
/* interpret or immediate in compiled code */
@ -264,16 +275,18 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, const struct fh_word_s *w)
return FH_OK;
}
static struct fh_word_s *find_word(struct fh_thread_s *fh, const char *name, const size_t wordlen)
static struct fh_word_s *find_word(struct fh_thread_s *fh, const char *name, const size_t wordlen, uint32_t *addr_out)
{
struct fh_word_s *w = &fh->dict[0];
uint32_t cnt = 0;
while (w->handler) {
uint32_t addr = fh->dict_last;
while (addr != MAGICADDR_DICTFIRST) {
struct fh_word_s *w = fh_word_at(fh, addr);
if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) {
if (addr_out) {
*addr_out = addr;
}
return w;
}
w++;
cnt++;
addr = w->previous;
}
return NULL;
}
@ -292,9 +305,10 @@ enum fh_error fh_handle_ascii_word(
/* First, try if it's a known word */
struct fh_word_s *w = find_word(fh, name, wordlen);
if (w) {// word found!
TRY(fh_handle_word(fh, w));
uint32_t wadr = MAGICADDR_UNRESOLVED;
find_word(fh, name, wordlen, &wadr);
if (wadr != MAGICADDR_UNRESOLVED) {
TRY(fh_handle_word(fh, wadr));
return FH_OK;
}
@ -316,7 +330,7 @@ enum fh_error fh_handle_ascii_word(
long v = strtol(name, &endptr, base); // XXX if base is 0, this will use auto-detection
if (errno != 0 || (endptr - name) != wordlen) {
LOGE("Unknown word and fail to parse as number: %.*s", (int) wordlen, name);
LOGE("Unknown word and fail to parse as number: \"%.*s\"", (int) wordlen, name);
return FH_ERR_UNKNOWN_WORD;
}
@ -337,7 +351,7 @@ enum fh_error fh_handle_ascii_word(
static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
{
if (w->handler == w_user_word) {
uint32_t execptr = w->start;
uint32_t execptr = w->param;
instr:;
// make sure it's aligned
@ -355,7 +369,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
goto instr;
case FH_INSTR_WORD:
w2 = &fh->dict[instr->data];
w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) {
FHPRINT("Call(%s)\n", w2->name);
} else {
@ -364,7 +378,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
goto instr;
case FH_INSTR_POSTPONED_WORD:
w2 = &fh->dict[instr->data];
w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) {
FHPRINT("Postpone(%s)\n", w2->name);
} else {
@ -410,7 +424,7 @@ static enum fh_error fh_see_word(
const size_t wordlen
)
{
struct fh_word_s *w = find_word(fh, name, wordlen);
struct fh_word_s *w = find_word(fh, name, wordlen, NULL);
if (!w) {
return FH_ERR_UNKNOWN_WORD;
}
@ -425,7 +439,8 @@ static enum fh_error fh_postpone_word(
const size_t wordlen
)
{
struct fh_word_s *w = find_word(fh, name, wordlen);
uint32_t wadr;
struct fh_word_s *w = find_word(fh, name, wordlen, &wadr);
if (!w) {
return FH_ERR_UNKNOWN_WORD;
}
@ -433,7 +448,7 @@ static enum fh_error fh_postpone_word(
enum fh_error rv;
struct fh_instruction_s instr;
LOG("Postpone %s", w->name);
instr_init(&instr, FH_INSTR_POSTPONED_WORD, w->index);
instr_init(&instr, FH_INSTR_POSTPONED_WORD, wadr);
TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK;
@ -491,7 +506,7 @@ enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf)
case FH_SUBSTATE_COLON_NAME:
/* new word's name is found */
LOG("New word name = \"%.*s\"", (int) length, rp);
strncpy(fh->dict[fh->dict_top].name, rp, length);
strncpy(fh_word_at(fh, fh->dict_last)->name, rp, length);
fh_setsubstate(fh, FH_SUBSTATE_NONE);
break;
case FH_SUBSTATE_SEE_NAME:

@ -10,14 +10,6 @@
int main(int argc, char *argv[])
{
enum fh_error rv;
struct fh_thread_s fh;
rv = fh_init(&fh);
if (rv != FH_OK) {
LOGE("Error in forth init: %s", fherr_name(rv));
return 1;
}
fh_globals.verbose = false;
fh_globals.interactive = isatty(STDIN_FILENO);
@ -49,6 +41,14 @@ int main(int argc, char *argv[])
}
}
enum fh_error rv;
struct fh_thread_s fh;
rv = fh_init(&fh);
if (rv != FH_OK) {
LOGE("Error in forth init: %s", fherr_name(rv));
return 1;
}
const char *prompt = "> ";
/* process input line by line */

Loading…
Cancel
Save