merged dict with heap
This commit is contained in:
+5
-11
@@ -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
|
||||
|
||||
+18
-15
@@ -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
|
||||
|
||||
+27
-43
@@ -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) {
|
||||
|
||||
+31
-1
@@ -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];
|
||||
}
|
||||
|
||||
+45
-30
@@ -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:
|
||||
|
||||
+8
-8
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user