#include "forth_internal.h" void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput) { LOG("--- Push input spec ---"); 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; // oldinput->saved_state = fh->state; oldinput->saved_execptr = fh->execptr; newinput->previous = oldinput; fh->inputlen = 0; fh->inputptr = 0; // fh_setstate(fh, FH_STATE_INTERPRET, 0); fh->input = newinput; } void fh_pop_input(struct fh_thread_s *fh) { LOG("--- Pop input spec ---"); 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; fh->execptr = restored->saved_execptr; // fh_setstate(fh, restored->saved_state, 0); 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 - 1; 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); LOG("read %d bytes from file", fh->inputlen); 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 = INPUT_BUFFER_SIZE - fh->inputlen - 1; 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); *(wp + space_left) = 0; fis->readpos += space_left; fh->inputlen += 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; } }