From ab1b39598c49497712b5bb6013093af98a4fc145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 22 Nov 2021 00:07:53 +0100 Subject: [PATCH] convert input parsing to pluggable specs --- CMakeLists.txt | 1 + include/fh_config.h | 2 + include/fh_input.h | 46 ++++++++++ include/fh_runtime.h | 7 +- include/forth.h | 1 + include/forth_internal.h | 1 + src/fh_input.c | 191 +++++++++++++++++++++++++++++++++++++++ src/fh_parse.c | 70 +++++++++++++- src/main.c | 48 +--------- 9 files changed, 316 insertions(+), 51 deletions(-) create mode 100644 include/fh_input.h create mode 100644 src/fh_input.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 31214a7..c913720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(forth src/fh_error.c src/fh_see.c src/fh_parse.c + src/fh_input.c ) target_include_directories(forth PRIVATE include) diff --git a/include/fh_config.h b/include/fh_config.h index 0daf892..7d971ca 100644 --- a/include/fh_config.h +++ b/include/fh_config.h @@ -34,4 +34,6 @@ #define MAGICADDR_UNRESOLVED 0xFFFFFBADULL #define MAGICADDR_ENDCASE_UNRESOLVED 0xFFFC5BADULL +#define FH_PROMPT_STR "> " + #endif //FORTH_FH_CONFIG_H diff --git a/include/fh_input.h b/include/fh_input.h new file mode 100644 index 0000000..ce15a72 --- /dev/null +++ b/include/fh_input.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 diff --git a/include/fh_runtime.h b/include/fh_runtime.h index d763b19..6a08104 100644 --- a/include/fh_runtime.h +++ b/include/fh_runtime.h @@ -217,6 +217,9 @@ struct fh_thread_s { uint32_t parse_if_level; 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); @@ -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_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) { diff --git a/include/forth.h b/include/forth.h index 9a2a296..4bb492f 100644 --- a/include/forth.h +++ b/include/forth.h @@ -14,6 +14,7 @@ #include "fh_config.h" #include "fh_error.h" #include "fh_globals.h" +#include "fh_input.h" #include "fh_runtime.h" #include "fh_print.h" diff --git a/include/forth_internal.h b/include/forth_internal.h index b28c8f6..bc974b4 100644 --- a/include/forth_internal.h +++ b/include/forth_internal.h @@ -23,6 +23,7 @@ #include "fh_helpers.h" #include "fh_globals.h" #include "fh_print.h" +#include "fh_input.h" #include "fh_runtime.h" #include "fh_mem.h" #include "fh_stack.h" diff --git a/src/fh_input.c b/src/fh_input.c new file mode 100644 index 0000000..db40413 --- /dev/null +++ b/src/fh_input.c @@ -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; + } +} diff --git a/src/fh_parse.c b/src/fh_parse.c index 114eef1..234397b 100644 --- a/src/fh_parse.c +++ b/src/fh_parse.c @@ -221,8 +221,72 @@ enum fh_error fh_input_read_quotedstring(struct fh_thread_s *fh, bool escaped, c 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 */ -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; @@ -230,12 +294,12 @@ enum fh_error fh_process_line(struct fh_thread_s *fh, const char *linebuf, size_ #define ReadPos (fh->inputptr) #define ReadLen (fh->inputlen) - fh_fill_input_buffer(fh, linebuf, len); + //fh_fill_input_buffer(fh, linebuf, len); char c; if (fh_globals.echo && !fh_globals.interactive) { - LOGI("%s", linebuf); + LOGI("%.*s", ReadLen, ReadPtr); } while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) { diff --git a/src/main.c b/src/main.c index 72c14d9..e4eb022 100644 --- a/src/main.c +++ b/src/main.c @@ -63,53 +63,7 @@ int main(int argc, char *argv[]) return 1; } - const char *prompt = "> "; - - /* 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); - } - } + fh_runtime_start(&fh, fh_create_input_from_filestruct(infile)); // Show resource usage LOG("\nResources used: DS %dW, RS %dW, memory %dB\n",