|
|
@ -1,14 +1,4 @@ |
|
|
|
#include <string.h> |
|
|
|
#include "forth_internal.h" |
|
|
|
#include <errno.h> |
|
|
|
|
|
|
|
#include <ctype.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "fh_error.h" |
|
|
|
|
|
|
|
#include "fh_runtime.h" |
|
|
|
|
|
|
|
#include "fh_builtins.h" |
|
|
|
|
|
|
|
#include "fh_stack.h" |
|
|
|
|
|
|
|
#include "fh_mem.h" |
|
|
|
|
|
|
|
#include "fh_globals.h" |
|
|
|
|
|
|
|
#include "fh_print.h" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct fh_global_s fh_globals = {}; |
|
|
|
struct fh_global_s fh_globals = {}; |
|
|
|
|
|
|
|
|
|
|
@ -29,11 +19,12 @@ static const char *stateshort[FH_STATE_MAX] = { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** Sub-state names */ |
|
|
|
/** Sub-state names */ |
|
|
|
static const char *substatenames[FH_SUBSTATE_MAX] = { |
|
|
|
const char *substatenames[FH_SUBSTATE_MAX] = { |
|
|
|
[FH_SUBSTATE_NONE] = "NONE", |
|
|
|
[FH_SUBSTATE_NONE] = "NONE", |
|
|
|
[FH_SUBSTATE_PAREN_COMMENT] = "PAREN_COMMENT", |
|
|
|
[FH_SUBSTATE_PAREN_COMMENT] = "PAREN_COMMENT", |
|
|
|
[FH_SUBSTATE_LINE_COMMENT] = "LINE_COMMENT", |
|
|
|
[FH_SUBSTATE_LINE_COMMENT] = "LINE_COMMENT", |
|
|
|
[FH_SUBSTATE_EXIT] = "EXIT", |
|
|
|
[FH_SUBSTATE_EXIT] = "EXIT", |
|
|
|
|
|
|
|
[FH_SUBSTATE_SKIP_IF] = "SKIP_IF", |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** Sub-state names */ |
|
|
|
/** Sub-state names */ |
|
|
@ -76,7 +67,9 @@ enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh) |
|
|
|
//LOG("Added word \"%s\" at 0x%08x", w->name, ptr);
|
|
|
|
//LOG("Added word \"%s\" at 0x%08x", w->name, ptr);
|
|
|
|
|
|
|
|
|
|
|
|
// thread it onto the linked list
|
|
|
|
// thread it onto the linked list
|
|
|
|
fh_word_at(fh, ptr)->previous = fh->dict_last; |
|
|
|
struct fh_word_s *word = fh_word_at(fh, ptr); |
|
|
|
|
|
|
|
if (!word) return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
word->previous = fh->dict_last; |
|
|
|
fh->dict_last = ptr; |
|
|
|
fh->dict_last = ptr; |
|
|
|
|
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
@ -132,6 +125,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
// make sure it's aligned
|
|
|
|
// make sure it's aligned
|
|
|
|
fh->execptr = WORDALIGNED(fh->execptr); |
|
|
|
fh->execptr = WORDALIGNED(fh->execptr); |
|
|
|
const struct fh_instruction_s *instr = fh_instr_at(fh, fh->execptr); |
|
|
|
const struct fh_instruction_s *instr = fh_instr_at(fh, fh->execptr); |
|
|
|
|
|
|
|
if (!instr) { |
|
|
|
|
|
|
|
LOGE("Execution pointer out of bounds!"); |
|
|
|
|
|
|
|
return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
} |
|
|
|
fh->execptr += INSTR_SIZE; |
|
|
|
fh->execptr += INSTR_SIZE; |
|
|
|
|
|
|
|
|
|
|
|
uint32_t strl; |
|
|
|
uint32_t strl; |
|
|
@ -145,8 +142,12 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
goto instr; |
|
|
|
goto instr; |
|
|
|
|
|
|
|
|
|
|
|
case FH_INSTR_POSTPONED_WORD: |
|
|
|
case FH_INSTR_POSTPONED_WORD: |
|
|
|
if (fh->state == FH_STATE_COMPILE) { |
|
|
|
//if (fh->state == FH_STATE_COMPILE) {
|
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
|
|
|
|
if (!w2) { |
|
|
|
|
|
|
|
LOGE("Postponed bad word addr!"); |
|
|
|
|
|
|
|
return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
} |
|
|
|
if (w2->flags & WORDFLAG_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)); |
|
|
@ -154,17 +155,21 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
LOG("Add postponed word: %s", w2->name); |
|
|
|
LOG("Add postponed word: %s", w2->name); |
|
|
|
TRY(fh_put_instr(fh, FH_INSTR_WORD, instr->data)); |
|
|
|
TRY(fh_put_instr(fh, FH_INSTR_WORD, instr->data)); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
/*} else {
|
|
|
|
LOGE("Postpone in interpret mode!"); |
|
|
|
LOGE("Postpone in interpret mode!"); |
|
|
|
goto end; |
|
|
|
goto end; |
|
|
|
} |
|
|
|
}*/ |
|
|
|
goto instr; |
|
|
|
goto instr; |
|
|
|
|
|
|
|
|
|
|
|
case FH_INSTR_WORD: |
|
|
|
case FH_INSTR_WORD: |
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
|
|
|
|
if (!w2) { |
|
|
|
|
|
|
|
LOGE("Instr bad word addr!"); |
|
|
|
|
|
|
|
return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
} |
|
|
|
if (w2->flags & WORDFLAG_BUILTIN) { |
|
|
|
if (w2->flags & WORDFLAG_BUILTIN) { |
|
|
|
LOG("Exec: native-word \"%s\"", w2->name); |
|
|
|
LOG("Exec: native-word \"%s\"", w2->name); |
|
|
|
w2->handler(fh, w2); |
|
|
|
TRY(w2->handler(fh, w2)); |
|
|
|
if (fh->substate == FH_SUBSTATE_EXIT) { |
|
|
|
if (fh->substate == FH_SUBSTATE_EXIT) { |
|
|
|
fh_setsubstate(fh, 0); |
|
|
|
fh_setsubstate(fh, 0); |
|
|
|
LOG("Exec: early return"); |
|
|
|
LOG("Exec: early return"); |
|
|
@ -177,6 +182,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
LOG("Exec: user-word %s (CALL)", w2->name); |
|
|
|
LOG("Exec: user-word %s (CALL)", w2->name); |
|
|
|
w = fh_word_at(fh, instr->data); |
|
|
|
w = fh_word_at(fh, instr->data); |
|
|
|
|
|
|
|
if (!w) { |
|
|
|
|
|
|
|
LOGE("CALL instr bad word addr!"); |
|
|
|
|
|
|
|
return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
} |
|
|
|
goto call; |
|
|
|
goto call; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -203,6 +212,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
case FH_INSTR_TO: |
|
|
|
case FH_INSTR_TO: |
|
|
|
TRY(ds_pop(fh, &val)); |
|
|
|
TRY(ds_pop(fh, &val)); |
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
w2 = fh_word_at(fh, instr->data); |
|
|
|
|
|
|
|
if (!w2) { |
|
|
|
|
|
|
|
LOGE("TO instr bad variable addr!"); |
|
|
|
|
|
|
|
return FH_ERR_INTERNAL; |
|
|
|
|
|
|
|
} |
|
|
|
LOG("Exec: %d->%s", val, w2->name); |
|
|
|
LOG("Exec: %d->%s", val, w2->name); |
|
|
|
|
|
|
|
|
|
|
|
if (w2->flags & WORDFLAG_CONSTANT) { |
|
|
|
if (w2->flags & WORDFLAG_CONSTANT) { |
|
|
@ -255,7 +268,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
|
|
|
|
|
|
|
|
LOG("after add: %d", fh->loop_i); |
|
|
|
LOG("after add: %d", fh->loop_i); |
|
|
|
|
|
|
|
|
|
|
|
if (((int32_t)index0 < (int32_t)limit) == ((int32_t)fh->loop_i < (int32_t)limit)) { // boundary not crossed, continue
|
|
|
|
// FIXME this is probably wrong
|
|
|
|
|
|
|
|
if (((int32_t)index0 < (int32_t)limit) == ((int32_t)fh->loop_i < (int32_t)limit) && fh->loop_i != limit) { // boundary not crossed, continue
|
|
|
|
fh->execptr = instr->data; // go to beginning
|
|
|
|
fh->execptr = instr->data; // go to beginning
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// end of loop
|
|
|
|
// end of loop
|
|
|
@ -290,7 +304,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0) |
|
|
|
goto instr; |
|
|
|
goto instr; |
|
|
|
|
|
|
|
|
|
|
|
case FH_INSTR_ENDWORD: |
|
|
|
case FH_INSTR_ENDWORD: |
|
|
|
LOG("Exec: word-end (RETURN)"); |
|
|
|
LOG("Exec: word-end"); |
|
|
|
TRY(rs_pop(fh, &fh->execptr)); |
|
|
|
TRY(rs_pop(fh, &fh->execptr)); |
|
|
|
if (fh->execptr == MAGICADDR_EXEC_INTERACTIVE) { |
|
|
|
if (fh->execptr == MAGICADDR_EXEC_INTERACTIVE) { |
|
|
|
goto end; |
|
|
|
goto end; |
|
|
@ -323,6 +337,7 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr) |
|
|
|
{ |
|
|
|
{ |
|
|
|
enum fh_error rv; |
|
|
|
enum fh_error rv; |
|
|
|
struct fh_word_s *w = fh_word_at(fh, addr); |
|
|
|
struct fh_word_s *w = fh_word_at(fh, addr); |
|
|
|
|
|
|
|
if (!w) return FH_ERR_INTERNAL; |
|
|
|
if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) { |
|
|
|
if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) { |
|
|
|
LOG("\x1b[34m[%s] Compile word:\x1b[m %s", stateshort[fh->state], w->name); |
|
|
|
LOG("\x1b[34m[%s] Compile word:\x1b[m %s", stateshort[fh->state], w->name); |
|
|
|
TRY(fh_put_instr(fh, FH_INSTR_WORD, addr)); |
|
|
|
TRY(fh_put_instr(fh, FH_INSTR_WORD, addr)); |
|
|
@ -341,12 +356,18 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr) |
|
|
|
|
|
|
|
|
|
|
|
enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out) |
|
|
|
enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
if (name == NULL) { |
|
|
|
|
|
|
|
return FH_ERR_UNKNOWN_WORD; |
|
|
|
|
|
|
|
} |
|
|
|
if (wordlen == 0) { |
|
|
|
if (wordlen == 0) { |
|
|
|
wordlen = strlen(name); |
|
|
|
wordlen = strlen(name); |
|
|
|
} |
|
|
|
} |
|
|
|
uint32_t addr = fh->dict_last; |
|
|
|
uint32_t addr = fh->dict_last; |
|
|
|
while (addr != MAGICADDR_DICTFIRST) { |
|
|
|
while (addr != MAGICADDR_DICTFIRST) { |
|
|
|
struct fh_word_s *w = fh_word_at(fh, addr); |
|
|
|
struct fh_word_s *w = fh_word_at(fh, addr); |
|
|
|
|
|
|
|
if (!w) { |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
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) { |
|
|
|
if (addr_out) { |
|
|
|
*addr_out = addr; |
|
|
|
*addr_out = addr; |
|
|
@ -377,61 +398,6 @@ enum fh_error fh_loop_unnest(struct fh_thread_s *fh) |
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Process a word read from input */ |
|
|
|
|
|
|
|
static enum fh_error fh_handle_ascii_word( |
|
|
|
|
|
|
|
struct fh_thread_s *fh, |
|
|
|
|
|
|
|
const char *name, |
|
|
|
|
|
|
|
const size_t wordlen |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
enum fh_error rv; |
|
|
|
|
|
|
|
if (wordlen >= MAX_NAME_LEN) { |
|
|
|
|
|
|
|
return FH_ERR_NAME_TOO_LONG; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* First, try if it's a known word */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t wadr = 0; |
|
|
|
|
|
|
|
if (FH_OK == fh_find_word(fh, name, wordlen, &wadr)) { |
|
|
|
|
|
|
|
TRY(fh_handle_word(fh, wadr)); |
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* word not found, try parsing as number */ |
|
|
|
|
|
|
|
errno = 0; |
|
|
|
|
|
|
|
char *endptr; |
|
|
|
|
|
|
|
int base = (int) fh->base; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// prefix can override BASE - this is a syntax extension
|
|
|
|
|
|
|
|
if (name[0] == '0') { |
|
|
|
|
|
|
|
if (name[1] == 'x') { |
|
|
|
|
|
|
|
base = 16; |
|
|
|
|
|
|
|
} else if (name[1] == 'b') { |
|
|
|
|
|
|
|
base = 2; |
|
|
|
|
|
|
|
} else if (name[1] == 'o') { |
|
|
|
|
|
|
|
base = 8; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
|
|
return FH_ERR_UNKNOWN_WORD; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct fh_instruction_s instr; |
|
|
|
|
|
|
|
if (fh->state == FH_STATE_COMPILE) { |
|
|
|
|
|
|
|
LOG("Compile number: %ld", v); |
|
|
|
|
|
|
|
TRY(fh_put_instr(fh, FH_INSTR_NUMBER, (uint32_t) v)); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
/* interpret */ |
|
|
|
|
|
|
|
LOG("Interpret number: %ld", v); |
|
|
|
|
|
|
|
TRY(ds_push(fh, (uint32_t) v)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Postpone a word */ |
|
|
|
/** Postpone a word */ |
|
|
|
enum fh_error fh_postpone_word( |
|
|
|
enum fh_error fh_postpone_word( |
|
|
@ -449,91 +415,3 @@ enum fh_error fh_postpone_word( |
|
|
|
|
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
return FH_OK; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** True if the character is CR or LF */ |
|
|
|
|
|
|
|
static inline bool isnl(char c) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return c == '\n' || c == '\r'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Process a line read from input */ |
|
|
|
|
|
|
|
enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_t len) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
enum fh_error rv; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define ReadPtr ((char*)(&fh->heap[INPUTBUF_ADDR + fh->inputptr])) |
|
|
|
|
|
|
|
#define ReadPos (fh->inputptr) |
|
|
|
|
|
|
|
#define ReadLen (fh->inputlen) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fh_fill_input_buffer(fh, linebuf, len); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char c; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (fh_globals.echo && !fh_globals.interactive) { |
|
|
|
|
|
|
|
LOGI("%s", linebuf); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) { |
|
|
|
|
|
|
|
c = *ReadPtr; |
|
|
|
|
|
|
|
/* end on newline */ |
|
|
|
|
|
|
|
if (isnl(c)) { |
|
|
|
|
|
|
|
goto done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/* skip whitespace */ |
|
|
|
|
|
|
|
if (isspace(c)) { |
|
|
|
|
|
|
|
ReadPos++; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const char * const rp = ReadPtr; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *end; |
|
|
|
|
|
|
|
size_t length; |
|
|
|
|
|
|
|
switch (fh->substate) { |
|
|
|
|
|
|
|
case FH_SUBSTATE_NONE: |
|
|
|
|
|
|
|
/* try to read a word */ |
|
|
|
|
|
|
|
end = strchr(rp, ' '); |
|
|
|
|
|
|
|
if (end) { |
|
|
|
|
|
|
|
length = end - rp; /* exclude the space */ |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
length = strlen(rp); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ReadPos += length + 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* eval a word */ |
|
|
|
|
|
|
|
//LOG("Handle \"%.*s\"", (int) length, rp);
|
|
|
|
|
|
|
|
TRY(fh_handle_ascii_word(fh, rp, length)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!end) { |
|
|
|
|
|
|
|
goto done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case FH_SUBSTATE_PAREN_COMMENT: |
|
|
|
|
|
|
|
end = strchr(rp, ')'); |
|
|
|
|
|
|
|
if (end) { |
|
|
|
|
|
|
|
length = end - rp; |
|
|
|
|
|
|
|
LOG("Discard inline comment"); |
|
|
|
|
|
|
|
fh_setsubstate(fh, FH_SUBSTATE_NONE); |
|
|
|
|
|
|
|
ReadPos += length + 1; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
/* no end, discard all */ |
|
|
|
|
|
|
|
LOGE("Unterminated parenthesis comment"); |
|
|
|
|
|
|
|
goto done; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case FH_SUBSTATE_LINE_COMMENT: |
|
|
|
|
|
|
|
LOG("Discard line comment"); |
|
|
|
|
|
|
|
fh_setsubstate(fh, 0); |
|
|
|
|
|
|
|
goto done; // just discard the rest
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
LOGE("Bad substate %s", substatenames[fh->substate]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
done: |
|
|
|
|
|
|
|
LOG("Line done."); |
|
|
|
|
|
|
|
return FH_OK; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|