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 #ifndef FORTH_FH_MEM_H
#define 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); 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); 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); 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); 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) { char *fh_str_at(struct fh_thread_s *fh, uint32_t addr);
return (char *) &fh->heap[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);
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];
}
#endif //FORTH_FH_MEM_H #endif //FORTH_FH_MEM_H

@ -94,11 +94,17 @@ enum fh_substate {
FH_SUBSTATE_MAX, 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 */ /** Word struct as they are stored in the dictionary */
struct fh_word_s { struct fh_word_s {
uint32_t index; /** Linked list pointer to previous word */
uint32_t previous;
/** Word name */ /** Word name */
char name[MAX_NAME_LEN]; char name[MAX_NAME_LEN]; // XXX this wastes RAM!
/** /**
* Handler function. * Handler function.
* Builtin functions use pre-defined native handlers. * Builtin functions use pre-defined native handlers.
@ -106,17 +112,14 @@ struct fh_word_s {
* bytecode at 'start' address of the compile-memory area. * bytecode at 'start' address of the compile-memory area.
*/ */
word_exec_t handler; word_exec_t handler;
/** Indicates that this is a built-in instruction and not a word call */ uint32_t flags;
bool builtin;
/** Indicates that this instruction should always be treated as interpreted */
bool immediate;
/** Start address in case of user words, or param for builtins */ /** Start address in case of user words, or param for builtins */
union { uint32_t param;
uint32_t start;
uint32_t param;
};
}; };
#define MAGICADDR_DICTFIRST 0xFFFFFFFFULL
#define DICTWORD_SIZE sizeof(struct fh_word_s)
/** /**
* Forth runtime instance - state variables and memory areas. * 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. * to a shared pointer if multi-threading and synchronization is added.
*/ */
struct fh_thread_s { struct fh_thread_s {
// TODO stacks could live on heap too and share space with some other structures
/** Data stack */ /** Data stack */
uint32_t data_stack[DATA_STACK_DEPTH]; uint32_t data_stack[DATA_STACK_DEPTH];
size_t data_stack_top; size_t data_stack_top;
@ -140,10 +145,8 @@ struct fh_thread_s {
/** Pointer into the compile buffer for execution */ /** Pointer into the compile buffer for execution */
uint32_t execptr; uint32_t execptr;
/** Address of the last dict word */
/** Word dict */ uint32_t dict_last;
struct fh_word_s dict[DICT_SIZE];
uint32_t dict_top;
/** Forth state */ /** Forth state */
enum fh_state state; enum fh_state state;
@ -193,6 +196,6 @@ enum fh_error fh_handle_ascii_word(
size_t wordlen 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 #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) static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
enum fh_error rv;
ENSURE_STATE(FH_STATE_INTERPRET); ENSURE_STATE(FH_STATE_INTERPRET);
fh_setstate(fh, FH_STATE_COMPILE, FH_SUBSTATE_COLON_NAME); fh_setstate(fh, FH_STATE_COMPILE, FH_SUBSTATE_COLON_NAME);
if (fh->dict_top >= DICT_SIZE) { uint32_t ptr;
return FH_ERR_DICT_FULL; TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr));
}
struct fh_word_s *new_word = &fh->dict[fh->dict_top]; struct fh_word_s *new_word = fh_word_at(fh, ptr);
new_word->index = fh->dict_top; new_word->previous = fh->dict_last;
new_word->start = fh->here; new_word->param = fh->here;
new_word->handler = w_user_word; new_word->handler = w_user_word;
fh->dict_last = ptr;
return FH_OK; 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 */ /* Return to interpret state */
fh_setstate(fh, FH_STATE_INTERPRET, 0); 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; 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; (void) w;
enum fh_error rv; enum fh_error rv;
if (fh->dict_top == 0) { if (fh->dict_last == 0) {
LOGE("Dict is empty, cannot modify previous word!"); LOGE("Dict is empty, cannot modify previous word!");
return FH_ERR_INVALID_STATE; 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; 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); 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)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; return FH_OK;
@ -1157,6 +1141,19 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
const struct name_and_handler builtins[] = { const struct name_and_handler builtins[] = {
{"", w_error_word0, 1, 0}, {"", 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 */ /* Strings & Chars */
{"s\"", w_s_quote, 1, 0}, {"s\"", w_s_quote, 1, 0},
{".\"", w_dot_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_semicolon, 1, 0},
{"\\", w_backslash, 1, 0}, // line comment {"\\", w_backslash, 1, 0}, // line comment
{"(", w_paren, 1, 0}, // enclosed 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 */ } { /* end marker */ }
}; };
LOG("Adding builtin words");
// foreach // foreach
struct fh_word_s w; struct fh_word_s w;
const struct name_and_handler *p = builtins; const struct name_and_handler *p = builtins;
enum fh_error rv; enum fh_error rv;
while (p->handler) { while (p->handler) {
strcpy(w.name, p->name); strcpy(w.name, p->name);
w.index = fh->dict_top;
w.handler = p->handler; w.handler = p->handler;
w.builtin = 1; w.flags = WORDFLAG_BUILTIN | (p->immediate ? WORDFLAG_IMMEDIATE : 0);
w.immediate = p->immediate;
w.param = p->param; w.param = p->param;
rv = fh_add_word(&w, fh); rv = fh_add_word(&w, fh);
if (rv != FH_OK) { if (rv != FH_OK) {

@ -5,6 +5,11 @@
#include "fh_runtime.h" #include "fh_runtime.h"
#include "fh_mem.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) { void fh_setbase(struct fh_thread_s *fh, uint32_t base) {
LOG("BASE = %d", base); LOG("BASE = %d", base);
fh->base = base; fh->base = base;
@ -105,6 +110,9 @@ enum fh_error fh_heap_reserve(
*addr = p; *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); fh->here = WORDALIGNED(p + len);
return FH_OK; 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 fh_heap_put(struct fh_thread_s *fh, const void *src, uint32_t len)
{ {
enum fh_error rv; enum fh_error rv;
uint32_t addr; uint32_t addr = 0;
TRY(fh_heap_reserve(fh, len, &addr)); TRY(fh_heap_reserve(fh, len, &addr));
fh_heap_write(fh, addr, src, len); fh_heap_write(fh, addr, src, len);
return FH_OK; 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); 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. */ /** Add a word to the dictionary. */
enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh) enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh)
{ {
if (fh->dict_top == DICT_SIZE) { enum fh_error rv;
return FH_ERR_DICT_FULL;
} fh_align(fh);
memcpy(&fh->dict[fh->dict_top++], w, sizeof(struct fh_word_s)); 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; 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); LOG("Run user word: %s", w->name);
TRY(rs_push(fh, fh->execptr)); TRY(rs_push(fh, fh->execptr));
fh->execptr = w->start; fh->execptr = w->param;
instr:; instr:;
if (fh->state == FH_STATE_QUIT) { 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: case FH_INSTR_POSTPONED_WORD:
if (fh->state == FH_STATE_COMPILE) { if (fh->state == FH_STATE_COMPILE) {
w2 = &fh->dict[instr->data]; w2 = fh_word_at(fh, instr->data);
if (w2->immediate) { if (w2->flags & WORDFLAG_IMMEDIATE) {
LOG("Call immediate postponed word: %s", w2->name); LOG("Call immediate postponed word: %s", w2->name);
TRY(w2->handler(fh, w2)); TRY(w2->handler(fh, w2));
} else { } else {
@ -124,8 +133,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
goto instr; goto instr;
case FH_INSTR_WORD: case FH_INSTR_WORD:
w2 = &fh->dict[instr->data]; w2 = fh_word_at(fh, instr->data);
if (w2->builtin) { if (w2->flags & WORDFLAG_BUILTIN) {
LOG("Exec: builtin-word \"%s\"", w2->name); LOG("Exec: builtin-word \"%s\"", w2->name);
w2->handler(fh, w2); w2->handler(fh, w2);
if (fh->substate == FH_SUBSTATE_EXIT) { 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; goto instr;
} else { } else {
LOG("Exec: user-word %s (CALL)", w2->name); LOG("Exec: user-word %s (CALL)", w2->name);
w = &fh->dict[instr->data]; w = fh_word_at(fh, instr->data);
goto call; goto call;
} }
@ -202,6 +211,7 @@ enum fh_error fh_init(struct fh_thread_s *fh)
/* Make sure we have a clean state */ /* Make sure we have a clean state */
memset(fh, 0, sizeof(struct fh_thread_s)); memset(fh, 0, sizeof(struct fh_thread_s));
fh->dict_last = MAGICADDR_DICTFIRST;
TRY(register_builtin_words(fh)); TRY(register_builtin_words(fh));
fh->execptr = MAGICADDR_INTERACTIVE; fh->execptr = MAGICADDR_INTERACTIVE;
@ -248,13 +258,14 @@ static enum fh_error fh_handle_quoted_string(
return FH_OK; 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; struct fh_instruction_s instr;
enum fh_error rv; 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); 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)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
} else { } else {
/* interpret or immediate in compiled code */ /* 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; 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 addr = fh->dict_last;
uint32_t cnt = 0; while (addr != MAGICADDR_DICTFIRST) {
while (w->handler) { struct fh_word_s *w = fh_word_at(fh, addr);
if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) { if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) {
if (addr_out) {
*addr_out = addr;
}
return w; return w;
} }
w++; addr = w->previous;
cnt++;
} }
return NULL; return NULL;
} }
@ -292,9 +305,10 @@ enum fh_error fh_handle_ascii_word(
/* First, try if it's a known word */ /* First, try if it's a known word */
struct fh_word_s *w = find_word(fh, name, wordlen); uint32_t wadr = MAGICADDR_UNRESOLVED;
if (w) {// word found! find_word(fh, name, wordlen, &wadr);
TRY(fh_handle_word(fh, w)); if (wadr != MAGICADDR_UNRESOLVED) {
TRY(fh_handle_word(fh, wadr));
return FH_OK; 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 long v = strtol(name, &endptr, base); // XXX if base is 0, this will use auto-detection
if (errno != 0 || (endptr - name) != wordlen) { 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; 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) static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
if (w->handler == w_user_word) { if (w->handler == w_user_word) {
uint32_t execptr = w->start; uint32_t execptr = w->param;
instr:; instr:;
// make sure it's aligned // 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; goto instr;
case FH_INSTR_WORD: case FH_INSTR_WORD:
w2 = &fh->dict[instr->data]; w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) { if (w2->name[0]) {
FHPRINT("Call(%s)\n", w2->name); FHPRINT("Call(%s)\n", w2->name);
} else { } else {
@ -364,7 +378,7 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
goto instr; goto instr;
case FH_INSTR_POSTPONED_WORD: case FH_INSTR_POSTPONED_WORD:
w2 = &fh->dict[instr->data]; w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) { if (w2->name[0]) {
FHPRINT("Postpone(%s)\n", w2->name); FHPRINT("Postpone(%s)\n", w2->name);
} else { } else {
@ -410,7 +424,7 @@ static enum fh_error fh_see_word(
const size_t wordlen 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) { if (!w) {
return FH_ERR_UNKNOWN_WORD; return FH_ERR_UNKNOWN_WORD;
} }
@ -425,7 +439,8 @@ static enum fh_error fh_postpone_word(
const size_t wordlen 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) { if (!w) {
return FH_ERR_UNKNOWN_WORD; return FH_ERR_UNKNOWN_WORD;
} }
@ -433,7 +448,7 @@ static enum fh_error fh_postpone_word(
enum fh_error rv; enum fh_error rv;
struct fh_instruction_s instr; struct fh_instruction_s instr;
LOG("Postpone %s", w->name); 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)); TRY(fh_heap_put(fh, &instr, INSTR_SIZE));
return FH_OK; 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: case FH_SUBSTATE_COLON_NAME:
/* new word's name is found */ /* new word's name is found */
LOG("New word name = \"%.*s\"", (int) length, rp); 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); fh_setsubstate(fh, FH_SUBSTATE_NONE);
break; break;
case FH_SUBSTATE_SEE_NAME: case FH_SUBSTATE_SEE_NAME:

@ -10,14 +10,6 @@
int main(int argc, char *argv[]) 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.verbose = false;
fh_globals.interactive = isatty(STDIN_FILENO); 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 = "> "; const char *prompt = "> ";
/* process input line by line */ /* process input line by line */

Loading…
Cancel
Save