convert input parsing to pluggable specs
This commit is contained in:
@@ -19,6 +19,7 @@ add_executable(forth
|
|||||||
src/fh_error.c
|
src/fh_error.c
|
||||||
src/fh_see.c
|
src/fh_see.c
|
||||||
src/fh_parse.c
|
src/fh_parse.c
|
||||||
|
src/fh_input.c
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(forth PRIVATE include)
|
target_include_directories(forth PRIVATE include)
|
||||||
|
|||||||
@@ -34,4 +34,6 @@
|
|||||||
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
|
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
|
||||||
#define MAGICADDR_ENDCASE_UNRESOLVED 0xFFFC5BADULL
|
#define MAGICADDR_ENDCASE_UNRESOLVED 0xFFFC5BADULL
|
||||||
|
|
||||||
|
#define FH_PROMPT_STR "> "
|
||||||
|
|
||||||
#endif //FORTH_FH_CONFIG_H
|
#endif //FORTH_FH_CONFIG_H
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* TODO file description
|
||||||
|
*
|
||||||
|
* Created on 2021/11/21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FORTH_FH_INPUT_H
|
||||||
|
#define FORTH_FH_INPUT_H
|
||||||
|
|
||||||
|
struct fh_thread_s;
|
||||||
|
struct fh_input_spec_s;
|
||||||
|
|
||||||
|
/** Refill the input buffer, returns false on failure / EOF */
|
||||||
|
typedef bool (*fh_input_refill_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec);
|
||||||
|
/** Spec free func */
|
||||||
|
typedef void (*fh_input_free_t)(void *spec);
|
||||||
|
|
||||||
|
struct fh_input_spec_s {
|
||||||
|
struct fh_input_spec_s *previous;
|
||||||
|
fh_input_refill_t refill_input_buffer;
|
||||||
|
fh_input_free_t free_self;
|
||||||
|
uint32_t linenum;
|
||||||
|
|
||||||
|
// saved values, filled when pushing
|
||||||
|
char saved_buffer[INPUT_BUFFER_SIZE];
|
||||||
|
uint32_t saved_inputptr;
|
||||||
|
uint32_t saved_inputlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push current input spec and state, replace with new one
|
||||||
|
*/
|
||||||
|
void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard current input spec, restore previous.
|
||||||
|
* fh->input will be NULL if this was the topmost one
|
||||||
|
*/
|
||||||
|
void fh_pop_input(struct fh_thread_s *fh);
|
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filename(char *path);
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f);
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len);
|
||||||
|
void fh_input_teardown(struct fh_thread_s *fh);
|
||||||
|
|
||||||
|
#endif //FORTH_FH_INPUT_H
|
||||||
@@ -217,6 +217,9 @@ struct fh_thread_s {
|
|||||||
uint32_t parse_if_level;
|
uint32_t parse_if_level;
|
||||||
|
|
||||||
bool executing_compiled;
|
bool executing_compiled;
|
||||||
|
|
||||||
|
/** Input spec */
|
||||||
|
struct fh_input_spec_s *input;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum fh_error fh_loop_nest(struct fh_thread_s *fh, uint32_t indexvalue);
|
enum fh_error fh_loop_nest(struct fh_thread_s *fh, uint32_t indexvalue);
|
||||||
@@ -265,7 +268,9 @@ enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t word
|
|||||||
|
|
||||||
enum fh_error fh_init(struct fh_thread_s *fh);
|
enum fh_error fh_init(struct fh_thread_s *fh);
|
||||||
|
|
||||||
enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_t len);
|
//enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_t len);
|
||||||
|
|
||||||
|
enum fh_error fh_runtime_start(struct fh_thread_s *fh, struct fh_input_spec_s *input);
|
||||||
|
|
||||||
static inline uint32_t word_addr(struct fh_thread_s *fh, const struct fh_word_s *w)
|
static inline uint32_t word_addr(struct fh_thread_s *fh, const struct fh_word_s *w)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "fh_config.h"
|
#include "fh_config.h"
|
||||||
#include "fh_error.h"
|
#include "fh_error.h"
|
||||||
#include "fh_globals.h"
|
#include "fh_globals.h"
|
||||||
|
#include "fh_input.h"
|
||||||
#include "fh_runtime.h"
|
#include "fh_runtime.h"
|
||||||
#include "fh_print.h"
|
#include "fh_print.h"
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "fh_helpers.h"
|
#include "fh_helpers.h"
|
||||||
#include "fh_globals.h"
|
#include "fh_globals.h"
|
||||||
#include "fh_print.h"
|
#include "fh_print.h"
|
||||||
|
#include "fh_input.h"
|
||||||
#include "fh_runtime.h"
|
#include "fh_runtime.h"
|
||||||
#include "fh_mem.h"
|
#include "fh_mem.h"
|
||||||
#include "fh_stack.h"
|
#include "fh_stack.h"
|
||||||
|
|||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
#include "forth_internal.h"
|
||||||
|
|
||||||
|
void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput)
|
||||||
|
{
|
||||||
|
if (newinput == NULL) {
|
||||||
|
LOGE("push input with NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fh_input_spec_s *oldinput = fh->input;
|
||||||
|
if (NULL == oldinput) {
|
||||||
|
// no previous input spec, just use the new one
|
||||||
|
fh->input = newinput;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fh->input = NULL;
|
||||||
|
|
||||||
|
memcpy(&oldinput->saved_buffer[0], &fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE);
|
||||||
|
oldinput->saved_inputlen = fh->inputlen;
|
||||||
|
oldinput->saved_inputptr = fh->inputptr;
|
||||||
|
newinput->previous = oldinput;
|
||||||
|
|
||||||
|
fh->input = newinput;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fh_pop_input(struct fh_thread_s *fh)
|
||||||
|
{
|
||||||
|
struct fh_input_spec_s *discarded = fh->input;
|
||||||
|
fh->input = NULL;
|
||||||
|
|
||||||
|
struct fh_input_spec_s *restored = discarded->previous;
|
||||||
|
if (!restored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fh->input = restored; // this can be NULL, that must be checked by caller.
|
||||||
|
|
||||||
|
memcpy(&fh->heap[INPUTBUF_ADDR], &restored->saved_buffer[0], INPUT_BUFFER_SIZE);
|
||||||
|
fh->inputlen = restored->saved_inputlen;
|
||||||
|
fh->inputptr = restored->saved_inputptr;
|
||||||
|
|
||||||
|
if (discarded->free_self) {
|
||||||
|
discarded->free_self(discarded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file_input_spec {
|
||||||
|
struct fh_input_spec_s spec;
|
||||||
|
FILE *file;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_input_spec {
|
||||||
|
struct fh_input_spec_s spec;
|
||||||
|
char *str;
|
||||||
|
size_t len;
|
||||||
|
size_t readpos;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint8_t *inputbuf_at(struct fh_thread_s *fh, size_t pos)
|
||||||
|
{
|
||||||
|
return &fh->heap[INPUTBUF_ADDR + pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
void fh_input_memmove_leftovers(struct fh_thread_s *fh)
|
||||||
|
{
|
||||||
|
if (fh->inputptr < fh->inputlen) {
|
||||||
|
// something is left
|
||||||
|
uint32_t remains = fh->inputlen - fh->inputptr;
|
||||||
|
if (remains > 0) {
|
||||||
|
LOG("Refill, reuse %d bytes left in buffer", remains);
|
||||||
|
memmove(inputbuf_at(fh, 0), inputbuf_at(fh, fh->inputptr), remains);
|
||||||
|
fh->inputptr = 0;
|
||||||
|
fh->inputlen = remains;
|
||||||
|
} else {
|
||||||
|
LOG("Refill, nothing reused (1)");
|
||||||
|
fh->inputptr = 0;
|
||||||
|
fh->inputlen = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG("Refill, nothing reused (2)");
|
||||||
|
fh->inputptr = 0;
|
||||||
|
fh->inputlen = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool file_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec)
|
||||||
|
{
|
||||||
|
struct file_input_spec *fis = (struct file_input_spec *) spec;
|
||||||
|
fh_input_memmove_leftovers(fh);
|
||||||
|
uint32_t space_left = INPUT_BUFFER_SIZE - fh->inputlen;
|
||||||
|
char *wp = (char *) inputbuf_at(fh, fh->inputptr);
|
||||||
|
LOG("spec %p, fgets %d", spec, space_left);
|
||||||
|
if (fgets(wp, (int) space_left, fis->file)) {
|
||||||
|
spec->linenum++;
|
||||||
|
fh->inputlen = strnlen(wp, INPUT_BUFFER_SIZE);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return fh->inputptr > fh->inputlen; // return false only if there is nothing left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool str_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec)
|
||||||
|
{
|
||||||
|
struct string_input_spec *fis = (struct string_input_spec *) spec;
|
||||||
|
fh_input_memmove_leftovers(fh);
|
||||||
|
uint32_t space_left = INPUTBUF_ADDR - fh->inputlen;
|
||||||
|
char *wp = (char *) inputbuf_at(fh, fh->inputptr);
|
||||||
|
|
||||||
|
uint32_t chars_remaining_in_string = fis->len - fis->readpos;
|
||||||
|
if (chars_remaining_in_string > 0) {
|
||||||
|
if (chars_remaining_in_string < space_left) {
|
||||||
|
space_left = chars_remaining_in_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(wp, &fis->str[fis->readpos], space_left);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_filespec(void *p)
|
||||||
|
{
|
||||||
|
struct file_input_spec *spec = (struct file_input_spec *) p;
|
||||||
|
if (spec->file != stdin) {
|
||||||
|
fclose(spec->file);
|
||||||
|
spec->file = NULL;
|
||||||
|
}
|
||||||
|
free(spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f)
|
||||||
|
{
|
||||||
|
struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1);
|
||||||
|
if (!spec) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec->spec.free_self = free;
|
||||||
|
spec->spec.refill_input_buffer = file_refill;
|
||||||
|
spec->file = f;
|
||||||
|
return (struct fh_input_spec_s*) spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filename(char *path)
|
||||||
|
{
|
||||||
|
struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1);
|
||||||
|
if (!spec) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f = fopen(path, "r");
|
||||||
|
if (!f) {
|
||||||
|
free(spec);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec->spec.free_self = free_filespec;
|
||||||
|
spec->spec.refill_input_buffer = file_refill;
|
||||||
|
spec->file = f;
|
||||||
|
return (struct fh_input_spec_s*) spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len)
|
||||||
|
{
|
||||||
|
struct string_input_spec *spec = calloc(sizeof(struct string_input_spec), 1);
|
||||||
|
if (!spec) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec->spec.free_self = free;
|
||||||
|
spec->spec.refill_input_buffer = str_refill;
|
||||||
|
spec->str = str;
|
||||||
|
spec->readpos = 0;
|
||||||
|
spec->len = len;
|
||||||
|
return (struct fh_input_spec_s*) spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fh_input_teardown(struct fh_thread_s *fh)
|
||||||
|
{
|
||||||
|
struct fh_input_spec_s *s = fh->input;
|
||||||
|
if (!s) return;
|
||||||
|
|
||||||
|
while (s) {
|
||||||
|
struct fh_input_spec_s *prev = s->previous;
|
||||||
|
if (s->free_self) {
|
||||||
|
s->free_self(s);
|
||||||
|
}
|
||||||
|
s = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
-3
@@ -221,8 +221,72 @@ enum fh_error fh_input_read_quotedstring(struct fh_thread_s *fh, bool escaped, c
|
|||||||
return FH_ERR_SYNTAX;
|
return FH_ERR_SYNTAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum fh_error fh_process_line(struct fh_thread_s *fh);
|
||||||
|
|
||||||
|
enum fh_error fh_runtime_start(struct fh_thread_s *fh, struct fh_input_spec_s *input)
|
||||||
|
{
|
||||||
|
enum fh_error rv;
|
||||||
|
fh_push_input(fh, input);
|
||||||
|
|
||||||
|
if (fh_globals.interactive) {
|
||||||
|
FHPRINT("%s", FH_PROMPT_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
LOG("Refill input buffer");
|
||||||
|
if (fh->input->refill_input_buffer(fh, fh->input)) {
|
||||||
|
// discard spaces at the end
|
||||||
|
while (isspace(fh->heap[INPUTBUF_ADDR + fh->inputlen - 1]) && fh->inputlen > 0) {
|
||||||
|
fh->heap[INPUTBUF_ADDR + fh->inputlen - 1] = 0;
|
||||||
|
fh->inputlen--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fh->inputlen == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = fh_process_line(fh);
|
||||||
|
|
||||||
|
if (rv == FH_OK) {
|
||||||
|
if (fh_globals.interactive || fh_globals.echo) {
|
||||||
|
FHPRINT_SVC(" ok\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGE("ERROR %s on line %d", fherr_name(rv), fh->input->linenum);
|
||||||
|
if (!fh_globals.interactive) {
|
||||||
|
if (fh_globals.rescue) {
|
||||||
|
fh_globals.interactive = 1;
|
||||||
|
fh_input_teardown(fh);
|
||||||
|
fh_push_input(fh, fh_create_input_from_filestruct(stdin));
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* reset state */
|
||||||
|
fh_setstate(fh, FH_STATE_INTERPRET, FH_SUBSTATE_NONE);
|
||||||
|
// reset stack pointers
|
||||||
|
fh->data_stack_top = 0;
|
||||||
|
fh->return_stack_top = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fh_globals.interactive) {
|
||||||
|
FHPRINT("%s", FH_PROMPT_STR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LOG("Pop input");
|
||||||
|
fh_pop_input(fh);
|
||||||
|
if (!fh->input) {
|
||||||
|
// we are done.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/** Process a line read from input */
|
/** 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 fh_process_line(struct fh_thread_s *fh)
|
||||||
{
|
{
|
||||||
enum fh_error rv;
|
enum fh_error rv;
|
||||||
|
|
||||||
@@ -230,12 +294,12 @@ enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_
|
|||||||
#define ReadPos (fh->inputptr)
|
#define ReadPos (fh->inputptr)
|
||||||
#define ReadLen (fh->inputlen)
|
#define ReadLen (fh->inputlen)
|
||||||
|
|
||||||
fh_fill_input_buffer(fh, linebuf, len);
|
//fh_fill_input_buffer(fh, linebuf, len);
|
||||||
|
|
||||||
char c;
|
char c;
|
||||||
|
|
||||||
if (fh_globals.echo && !fh_globals.interactive) {
|
if (fh_globals.echo && !fh_globals.interactive) {
|
||||||
LOGI("%s", linebuf);
|
LOGI("%.*s", ReadLen, ReadPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) {
|
while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) {
|
||||||
|
|||||||
+1
-47
@@ -63,53 +63,7 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *prompt = "> ";
|
fh_runtime_start(&fh, fh_create_input_from_filestruct(infile));
|
||||||
|
|
||||||
/* process input line by line */
|
|
||||||
int linecnt = 0;
|
|
||||||
char linebuf[MAXLINE];
|
|
||||||
if (fh_globals.interactive) {
|
|
||||||
FHPRINT("%s", prompt);
|
|
||||||
}
|
|
||||||
while (fh.state != FH_STATE_SHUTDOWN && fgets(linebuf, MAXLINE, infile)) {
|
|
||||||
linecnt++;
|
|
||||||
|
|
||||||
// trim
|
|
||||||
size_t end = strlen(linebuf) - 1;
|
|
||||||
while (isspace(linebuf[end])) {
|
|
||||||
linebuf[end] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!linebuf[0]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = fh_process_line(&fh, linebuf, strlen(linebuf));
|
|
||||||
if (rv == FH_OK) {
|
|
||||||
if (fh_globals.interactive || fh_globals.echo) {
|
|
||||||
FHPRINT_SVC(" ok\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGE("ERROR %s on line %d", fherr_name(rv), linecnt);
|
|
||||||
if (!fh_globals.interactive) {
|
|
||||||
if (fh_globals.rescue) {
|
|
||||||
fh_globals.interactive = 1;
|
|
||||||
infile = stdin;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* reset state */
|
|
||||||
fh_setstate(&fh, FH_STATE_INTERPRET, FH_SUBSTATE_NONE);
|
|
||||||
// reset stack pointers
|
|
||||||
fh.data_stack_top = 0;
|
|
||||||
fh.return_stack_top = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fh_globals.interactive) {
|
|
||||||
FHPRINT("%s", prompt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show resource usage
|
// Show resource usage
|
||||||
LOG("\nResources used: DS %dW, RS %dW, memory %dB\n",
|
LOG("\nResources used: DS %dW, RS %dW, memory %dB\n",
|
||||||
|
|||||||
Reference in New Issue
Block a user