diff --git a/README.md b/README.md index 0833a70..33c5d92 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Implemented and tested: CORE: ! ' ( * */ */MOD + +! +LOOP , - . ." # #> #S <# >BODY >NUMBER / /mod 0< 0= 1+ 1- 2! 2* 2/ 2@ 2DROP 2DUP 2OVER 2SWAP : ; < = > >IN >R ?DUP @ ABORT ABS ALIGN ALIGNED ALLOT AND BASE BEGIN BL C! C, C@ CELL CELL+ CELLS CHAR CHAR+ -CHARS CONSTANT COUNT CR CREATE DECIMAL DEPTH DO DOES> DROP DUP ELSE EMIT ENVIRONMENT? EXECUTE EXIT FILL FM/MOD FIND +CHARS CONSTANT COUNT CR CREATE DECIMAL DEPTH DO DOES> DROP DUP ELSE EMIT ENVIRONMENT? EVALUATE EXECUTE EXIT FILL FM/MOD FIND HERE HOLD I IF IMMEDIATE INVERT J LEAVE LITERAL LOOP LSHIFT M* MAX MIN MOD MOVE NEGATE OR OVER POSTPONE QUIT R> R@ RECURSE REPEAT ROT RSHIFT S>D S" SIGN SM/REM SOURCE SPACE SPACES STATE SWAP THEN TYPE U. U< UNTIL UM* UM/MOD UNLOOP VARIABLE WHILE WORD XOR [ ['] [CHAR] ] @@ -59,14 +59,14 @@ CORE-EXT: \ Other sets: -FORGET SEE BYE +FORGET SEE BYE INCLUDE INCLUDED ``` Missing: ``` CORE: -ABORT" ACCEPT EVALUATE KEY +ABORT" ACCEPT KEY CORE-EXT: ACTION-OF DEFER DEFER! DEFER@ IS PARSE PARSE-NAME REFILL RESTORE-INPUT SAVE-INPUT SOURCE-ID [COMPILE] diff --git a/include/fh_input.h b/include/fh_input.h index 96b54d5..8396783 100644 --- a/include/fh_input.h +++ b/include/fh_input.h @@ -20,6 +20,7 @@ struct fh_input_spec_s { fh_input_refill_t refill_input_buffer; fh_input_free_t free_self; uint32_t linenum; + char *cwd; // CWD of the input, used for relative includes. malloc'd (strdup) // saved values, filled when pushing char saved_buffer[INPUT_BUFFER_SIZE]; @@ -32,17 +33,17 @@ struct fh_input_spec_s { /** * 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); +enum fh_error 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); +enum fh_error 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); +struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f, const char *path); +struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len, const char *cwd); // cwd is strduped. void fh_input_teardown(struct fh_thread_s *fh); #endif //FORTH_FH_INPUT_H diff --git a/include/forth_internal.h b/include/forth_internal.h index f5dbc5d..7ba8111 100644 --- a/include/forth_internal.h +++ b/include/forth_internal.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include // PATH_MAX #include "fh_config.h" #include "fh_error.h" diff --git a/src/fh_builtins_meta.c b/src/fh_builtins_meta.c index 2a288f1..43538fb 100644 --- a/src/fh_builtins_meta.c +++ b/src/fh_builtins_meta.c @@ -602,7 +602,50 @@ static enum fh_error w_evaluate(struct fh_thread_s *fh, const struct fh_word_s * uint32_t addr, count; TRY(ds_pop_addr_len(fh, &addr, &count)); - fh_runtime_start(fh, fh_create_input_from_string(fh_str_at(fh, addr), count)); + fh_runtime_start(fh, fh_create_input_from_string(fh_str_at(fh, addr), count, fh->input->cwd)); + return FH_OK; +} + +static enum fh_error w_included(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + uint32_t addr, count; + TRY(ds_pop_addr_len(fh, &addr, &count)); + + if (count > 99) { + LOGE("Filename too long for INCLUDED"); + return FH_ERR_NOT_APPLICABLE; + } + + char tmp[100]; + strncpy(tmp, fh_str_at(fh, addr), count); + tmp[count] = 0; + + fh_runtime_start(fh, fh_create_input_from_filename(tmp)); + return FH_OK; +} + +static enum fh_error w_include(struct fh_thread_s *fh, const struct fh_word_s *w) +{ + (void) w; + enum fh_error rv; + + char *wordname; + size_t namelen = 0; + fh_input_consume_spaces(fh); + TRY(fh_input_read_word(fh, &wordname, &namelen)); + + if (namelen > 99) { + LOGE("Filename too long for INCLUDED"); + return FH_ERR_NOT_APPLICABLE; + } + + char tmp[100]; + strncpy(tmp, wordname, namelen); + tmp[namelen] = 0; + + fh_runtime_start(fh, fh_create_input_from_filename(tmp)); return FH_OK; } @@ -702,5 +745,7 @@ const struct name_and_handler fh_builtins_meta[] = { {"marker", w_marker, 0, 0}, {"compile,", w_compile_comma, 0, 0}, {"evaluate", w_evaluate, 0, 0}, + {"included", w_included, 0, 0}, + {"include", w_include, 0, 0}, { /* end marker */ } }; diff --git a/src/fh_input.c b/src/fh_input.c index c58dcd2..6480fee 100644 --- a/src/fh_input.c +++ b/src/fh_input.c @@ -1,38 +1,39 @@ #include "forth_internal.h" -void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput) +enum fh_error 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; + return FH_ERR_INTERNAL; // TODO IO } 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; + if (NULL != oldinput) { + memcpy(&oldinput->saved_buffer[0], &fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE); + oldinput->saved_inputlen = fh->inputlen; + oldinput->saved_inputptr = fh->inputptr; + oldinput->saved_execptr = fh->execptr; + newinput->previous = oldinput; + } fh->inputlen = 0; fh->inputptr = 0; // fh_setstate(fh, FH_STATE_INTERPRET, 0); fh->input = newinput; + + if (newinput->cwd) { + LOG("CD to %s", newinput->cwd); + chdir(newinput->cwd); + } + return FH_OK; } -void fh_pop_input(struct fh_thread_s *fh) +enum fh_error fh_pop_input(struct fh_thread_s *fh) { LOG("--- Pop input spec ---"); @@ -41,7 +42,7 @@ void fh_pop_input(struct fh_thread_s *fh) struct fh_input_spec_s *restored = discarded->previous; if (!restored) { - return; + return FH_OK; // topmost } fh->input = restored; // this can be NULL, that must be checked by caller. @@ -54,6 +55,12 @@ void fh_pop_input(struct fh_thread_s *fh) if (discarded->free_self) { discarded->free_self(discarded); } + + if (restored->cwd) { + LOG("CD to %s", restored->cwd); + chdir(restored->cwd); + } + return FH_OK; } struct file_input_spec { @@ -63,7 +70,7 @@ struct file_input_spec { struct string_input_spec { struct fh_input_spec_s spec; - char *str; + const char *str; size_t len; size_t readpos; }; @@ -137,36 +144,56 @@ static bool str_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec) 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; + struct file_input_spec *fis = (struct file_input_spec *) p; + if (fis->file != stdin) { + fclose(fis->file); + fis->file = NULL; + } + if (fis->spec.cwd) { + free(fis->spec.cwd); + fis->spec.cwd = NULL; } - free(spec); + free(fis); } -struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f) +static void free_strspec(void *p) { - struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1); - if (!spec) { + struct string_input_spec *sis = (struct string_input_spec *) p; + if (sis->spec.cwd) { + free(sis->spec.cwd); + sis->spec.cwd = NULL; + } + free(sis); +} + +struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f, const char *cwd) +{ + struct file_input_spec *fis = calloc(sizeof(struct file_input_spec), 1); + if (!fis) { + LOGE("Err alloc input spec struct"); return NULL; } - spec->spec.free_self = free; - spec->spec.refill_input_buffer = file_refill; - spec->file = f; - return (struct fh_input_spec_s*) spec; + fis->spec.free_self = free_filespec; + fis->spec.refill_input_buffer = file_refill; + fis->file = f; + if (cwd) { + fis->spec.cwd = strdup(cwd); + } + return (struct fh_input_spec_s*) fis; } 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) { + LOGE("Err alloc input spec struct"); return NULL; } FILE *f = fopen(path, "r"); if (!f) { + LOGE("Err open file \"%s\"", path); free(spec); return NULL; } @@ -174,22 +201,33 @@ struct fh_input_spec_s *fh_create_input_from_filename(char *path) spec->spec.free_self = free_filespec; spec->spec.refill_input_buffer = file_refill; spec->file = f; + + char pwd[PATH_MAX+1]; + realpath(path, pwd); + char *end = strrchr(pwd, '/'); + if (end) { + // add terminator + *end = 0; + } + spec->spec.cwd = strdup(pwd); + LOG("Input for file %s, path %s\n", path, pwd); return (struct fh_input_spec_s*) spec; } -struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len) +struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len, const char *cwd) { - struct string_input_spec *spec = calloc(sizeof(struct string_input_spec), 1); - if (!spec) { + struct string_input_spec *sis = calloc(sizeof(struct string_input_spec), 1); + if (!sis) { 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; + sis->spec.free_self = free_strspec; + sis->spec.refill_input_buffer = str_refill; + sis->str = str; + sis->readpos = 0; + sis->len = len; + sis->spec.cwd = cwd ? strdup(cwd) : NULL; + return (struct fh_input_spec_s*) sis; } void fh_input_teardown(struct fh_thread_s *fh) diff --git a/src/fh_parse.c b/src/fh_parse.c index b10f04b..c6c9fce 100644 --- a/src/fh_parse.c +++ b/src/fh_parse.c @@ -227,7 +227,7 @@ enum fh_error fh_runtime_start(struct fh_thread_s *fh, struct fh_input_spec_s *i { enum fh_error rv; void *original_input = fh->input; - fh_push_input(fh, input); + TRY(fh_push_input(fh, input)); if (fh_globals.interactive) { FHPRINT("%s", FH_PROMPT_STR); @@ -258,7 +258,7 @@ enum fh_error fh_runtime_start(struct fh_thread_s *fh, struct fh_input_spec_s *i if (fh_globals.rescue) { fh_globals.interactive = 1; fh_input_teardown(fh); - fh_push_input(fh, fh_create_input_from_filestruct(stdin)); + fh_push_input(fh, fh_create_input_from_filestruct(stdin, NULL)); } else { return 1; } @@ -276,7 +276,7 @@ enum fh_error fh_runtime_start(struct fh_thread_s *fh, struct fh_input_spec_s *i } else { LOG("Pop input"); - fh_pop_input(fh); + TRY(fh_pop_input(fh)); if (fh->input == original_input || !fh->input) { // we are done break; diff --git a/src/main.c b/src/main.c index e4eb022..9aa4fb1 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include "forth.h" @@ -13,6 +16,8 @@ int main(int argc, char *argv[]) fh_globals.echo = 0; FILE *infile = stdin; + char pwd[PATH_MAX+1]; + getcwd(pwd, PATH_MAX); // TODO use getopt for (int a = 1; a < argc; a++) { @@ -52,6 +57,13 @@ int main(int argc, char *argv[]) LOGE("Error opening infile: %s", argv[a]); return 1; } + + realpath(argv[a], pwd); + char *end = strrchr(pwd, '/'); + if (end) { + // add terminator + *end = 0; + } } } @@ -63,7 +75,7 @@ int main(int argc, char *argv[]) return 1; } - fh_runtime_start(&fh, fh_create_input_from_filestruct(infile)); + fh_runtime_start(&fh, fh_create_input_from_filestruct(infile, pwd)); // Show resource usage LOG("\nResources used: DS %dW, RS %dW, memory %dB\n", diff --git a/testfiles/_included.f b/testfiles/_included.f new file mode 100644 index 0000000..06c6128 --- /dev/null +++ b/testfiles/_included.f @@ -0,0 +1,2 @@ +." Included file" CR + diff --git a/testfiles/_included2.f b/testfiles/_included2.f new file mode 100644 index 0000000..8d4eb99 --- /dev/null +++ b/testfiles/_included2.f @@ -0,0 +1,2 @@ +." Included file 2" CR + diff --git a/testfiles/include.f b/testfiles/include.f new file mode 100644 index 0000000..7ed8e0a --- /dev/null +++ b/testfiles/include.f @@ -0,0 +1,6 @@ +." main file" CR +S" _included.f" INCLUDED +." main file again" CR +INCLUDE _included2.f +." main file ends" +CR