#include #include #include "fh_runtime.h" #include "fh_config.h" #include "fh_error.h" #include "fh_print.h" #include "fh_builtins.h" #include "fh_stack.h" #include "fh_mem.h" #define TOBOOL(a) (a == 0 ? 0 : 0xFFFFFFFF) /** * Encode a code point using UTF-8 * * Copied from ESPTERM source * * @param out - output buffer (min 4 characters), will be 0-terminated if shorten than 4 * @param utf - code point 0-0x10FFFF * @return number of bytes on success, 0 on failure (also produces U+FFFD, which uses 3 bytes) */ static int utf8_encode(char *out, uint32_t utf) { if (utf <= 0x7F) { // Plain ASCII out[0] = (char) utf; out[1] = 0; return 1; } else if (utf <= 0x07FF) { // 2-byte unicode out[0] = (char) (((utf >> 6) & 0x1F) | 0xC0); out[1] = (char) (((utf >> 0) & 0x3F) | 0x80); out[2] = 0; return 2; } else if (utf <= 0xFFFF) { // 3-byte unicode out[0] = (char) (((utf >> 12) & 0x0F) | 0xE0); out[1] = (char) (((utf >> 6) & 0x3F) | 0x80); out[2] = (char) (((utf >> 0) & 0x3F) | 0x80); out[3] = 0; return 3; } else if (utf <= 0x10FFFF) { // 4-byte unicode out[0] = (char) (((utf >> 18) & 0x07) | 0xF0); out[1] = (char) (((utf >> 12) & 0x3F) | 0x80); out[2] = (char) (((utf >> 6) & 0x3F) | 0x80); out[3] = (char) (((utf >> 0) & 0x3F) | 0x80); // out[4] = 0; return 4; } else { // error - use replacement character out[0] = (char) 0xEF; out[1] = (char) 0xBF; out[2] = (char) 0xBD; out[3] = 0; return 0; } } static enum fh_error w_plus(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(ds_push(fh, a + b)); return FH_OK; } static enum fh_error w_minus(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(ds_push(fh, a - b)); return FH_OK; } static enum fh_error w_star(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(ds_push(fh, a * b)); return FH_OK; } static enum fh_error w_zero_less(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a < 0))); return FH_OK; } static enum fh_error w_zero_greater(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a > 0))); return FH_OK; } static enum fh_error w_zero_equals(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a == 0))); return FH_OK; } static enum fh_error w_zero_not_equals(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a != 0))); return FH_OK; } static enum fh_error w_less(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a < b))); return FH_OK; } static enum fh_error w_greater(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a > b))); return FH_OK; } static enum fh_error w_equals(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a == b))); return FH_OK; } static enum fh_error w_not_equals(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); TRY(ds_push(fh, TOBOOL(a != b))); return FH_OK; } static enum fh_error wp_add(struct fh_thread_s *fh, const struct fh_word_s *w) { enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, a + w->param)); return FH_OK; } static enum fh_error wp_mul(struct fh_thread_s *fh, const struct fh_word_s *w) { enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, a * w->param)); return FH_OK; } static enum fh_error wp_div(struct fh_thread_s *fh, const struct fh_word_s *w) { enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_push(fh, a * w->param)); return FH_OK; } static enum fh_error w_star_slash(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0, c = 0; TRY(ds_pop(fh, &c)); TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); if (c == 0) { return FH_ERR_DIV_BY_ZERO; } uint64_t v = ((uint64_t) a * (uint64_t) b) / (uint64_t) c; TRY(ds_push(fh, (uint32_t) v)); return FH_OK; } static enum fh_error w_slash(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); if (b == 0) { return FH_ERR_DIV_BY_ZERO; } TRY(ds_push(fh, a / b)); return FH_OK; } static enum fh_error w_slash_mod(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0, b = 0; TRY(ds_pop(fh, &b)); TRY(ds_pop(fh, &a)); if (b == 0) { return FH_ERR_DIV_BY_ZERO; } uint32_t rem = a % b; uint32_t div = a / b; TRY(ds_push(fh, rem)); TRY(ds_push(fh, div)); return FH_OK; } static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; if (fh->state != FH_STATE_INTERPRET) { return FH_ERR_INVALID_STATE; } fh_setstate(fh, FH_STATE_COMPILE, FH_SUBSTATE_COLONNAME); if (fh->dict_top >= DICT_SIZE) { return FH_ERR_DICT_FULL; } fh->dict[fh->dict_top].start = fh->compile_top; fh->dict[fh->dict_top].handler = w_user_word; return FH_OK; } static enum fh_error w_semicolon(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; struct fh_instruction_s instr; if (fh->state != FH_STATE_COMPILE) { return FH_ERR_INVALID_STATE; } instr.kind = FH_INSTR_WORD; instr.data = CPLWORD_ENDWORD; TRY(fh_compile_put(fh, &instr, INSTR_SIZE)); /* Return to interpret state */ fh_setstate(fh, FH_STATE_INTERPRET, 0); fh->dict_top++; return FH_OK; } static enum fh_error w_dupe(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_peek(fh, &a)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_question_dupe(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_peek(fh, &a)); if (a) { TRY(ds_push(fh, a)); } return FH_OK; } static enum fh_error w_two_dup(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; uint32_t b = 0; TRY(ds_peek_n(fh, &a, 0)); TRY(ds_peek_n(fh, &b, 1)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_drop(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); return FH_OK; } static enum fh_error w_two_drop(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a)); return FH_OK; } static enum fh_error w_swap(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; TRY(ds_roll(fh, 1)); return FH_OK; } static enum fh_error w_two_swap(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; if (fh->data_stack_top < 4) { LOG("DS two-swap UNDERFLOW"); return FH_ERR_DS_UNDERFLOW; } uint32_t n = fh->data_stack_top - 4; uint32_t a = fh->data_stack[n]; uint32_t b = fh->data_stack[n + 1]; fh->data_stack[n] = fh->data_stack[n + 2]; fh->data_stack[n + 1] = fh->data_stack[n + 3]; fh->data_stack[n + 2] = a; fh->data_stack[n + 3] = b; return FH_OK; } static enum fh_error w_rot(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; TRY(ds_roll(fh, 2)); return FH_OK; } static enum fh_error w_over(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_peek_n(fh, &a, 1)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_two_over(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; uint32_t b = 0; TRY(ds_peek_n(fh, &a, 2)); TRY(ds_peek_n(fh, &b, 3)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_tuck(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; uint32_t b = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(ds_push(fh, a)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_pick(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t nth = 0; uint32_t a = 0; TRY(ds_pop(fh, &nth)); TRY(ds_peek_n(fh, &a, nth)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_roll(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t n = 0; TRY(ds_pop(fh, &n)); TRY(ds_roll(fh, n)); return FH_OK; } static enum fh_error w_to_r(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; TRY(ds_pop(fh, &a)); TRY(rs_push(fh, a)); return FH_OK; } static enum fh_error w_two_to_r(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; uint32_t b; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(rs_push(fh, b)); TRY(rs_push(fh, a)); return FH_OK; } static enum fh_error w_two_r_from(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; uint32_t b; TRY(rs_pop(fh, &a)); TRY(rs_pop(fh, &b)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_two_r_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; uint32_t b; TRY(rs_peek_n(fh, &a, 0)); TRY(rs_peek_n(fh, &b, 1)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_r_from(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; TRY(rs_pop(fh, &a)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_r_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; TRY(rs_peek(fh, &a)); TRY(ds_push(fh, a)); return FH_OK; } static enum fh_error w_dot(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a = 0; TRY(ds_pop(fh, &a)); FHPRINT("%d ", (int32_t) a); return FH_OK; } static enum fh_error w_type(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t count = 0, addr = 0; TRY(ds_pop(fh, &count)); TRY(ds_pop(fh, &addr)); FHPRINT("%.*s", count, &fh->heap[addr]); return FH_OK; } static enum fh_error wp_putc(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) fh; FHPRINT("%c", w->param); return FH_OK; } static enum fh_error w_space(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; (void) fh; FHPRINT(" "); return FH_OK; } static enum fh_error w_dump(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; (void) fh; FHPRINT("DS "); for (int i = 0; i < fh->data_stack_top; i++) { FHPRINT("%d ", fh->data_stack[i]); } FHPRINT("\nRS "); for (int i = 0; i < fh->return_stack_top; i++) { FHPRINT("%d ", fh->return_stack[i]); } FHPRINT("\n"); return FH_OK; } static enum fh_error w_abort(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh->data_stack_top = 0; fh->return_stack_top = 0; fh_setstate(fh, FH_STATE_QUIT, 0); return FH_OK; } static enum fh_error w_quit(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh->return_stack_top = 0; fh_setstate(fh, FH_STATE_QUIT, 0); return FH_OK; } static enum fh_error w_exit(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setsubstate(fh, FH_SUBSTATE_EXIT); return FH_OK; } static enum fh_error w_s_quote(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setsubstate(fh, FH_SUBSTATE_SQUOTE); return FH_OK; } static enum fh_error w_dot_quote(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setsubstate(fh, FH_SUBSTATE_DOTQUOTE); return FH_OK; } static enum fh_error w_backslash(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setsubstate(fh, FH_SUBSTATE_LINECOMMENT); return FH_OK; } static enum fh_error w_paren(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setsubstate(fh, FH_SUBSTATE_PARENCOMMENT); return FH_OK; } static enum fh_error w_bye(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; fh_setstate(fh, FH_STATE_SHUTDOWN, 0); return FH_OK; } static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w) { fh_setbase(fh, w->param); return FH_OK; } static enum fh_error w_emit(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t a; TRY(ds_pop(fh, &a)); char buf[5]; int num = utf8_encode(buf, a); FHPRINT("%.*s", num, buf); return FH_OK; } static enum fh_error wp_const(struct fh_thread_s *fh, const struct fh_word_s *w) { enum fh_error rv; TRY(ds_push(fh, w->param)); return FH_OK; } static enum fh_error w_depth(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; TRY(ds_push(fh, w->param)); return FH_OK; } static enum fh_error w_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t addr = 0; TRY(ds_pop(fh, &addr)); uint32_t val = 0; TRY(fh_fetch(fh, addr, &val)); TRY(ds_push(fh, val)); return FH_OK; } static enum fh_error w_store(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t addr = 0; TRY(ds_pop(fh, &addr)); uint32_t val = 0; TRY(ds_pop(fh, &val)); TRY(fh_store(fh, addr, val)); return FH_OK; } static enum fh_error w_two_store(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t addr = 0; TRY(ds_pop(fh, &addr)); uint32_t a = 0, b = 0; TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &b)); TRY(fh_store(fh, addr, a)); TRY(fh_store(fh, addr + CELL, b)); return FH_OK; } static enum fh_error w_two_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) { (void) w; enum fh_error rv; uint32_t addr = 0; TRY(ds_pop(fh, &addr)); uint32_t a = 0, b = 0; TRY(fh_fetch(fh, addr, &a)); TRY(fh_fetch(fh, addr + CELL, &b)); TRY(ds_push(fh, b)); TRY(ds_push(fh, a)); return FH_OK; } /** Add pointers to built-in word handlers to a runtime struct */ enum fh_error register_builtin_words(struct fh_thread_s *fh) { struct name_and_handler { const char *name; word_exec_t handler; bool immediate; uint32_t param; }; const struct name_and_handler builtins[] = { {"s\"", w_s_quote, 1, 0}, {".\"", w_dot_quote, 1, 0}, /* Compiler control words */ {"bye", w_bye, 0, 0}, /* Pointers */ {"@", w_fetch, 0, 0}, {"!", w_store, 0, 0}, {"2!", w_two_store, 0, 0}, {"2@", w_two_fetch, 0, 0}, // TODO +! // TODO pictured numbers (#) // TODO tick // TODO comma // TODO >BODY, >IN, >NUMBER /* Arithmetics */ {"decimal", wp_setbase, 0, 10}, {"hex", wp_setbase, 0, 16}, {"base", wp_const, 0, MAGICADDR_BASE}, {"depth", w_depth, 0, 0}, {"+", w_plus, 0, 0}, {"-", w_minus, 0, 0}, {"*", w_star, 0, 0}, {"*/", w_star_slash, 0, 0}, // TODO */mod {"/", w_slash, 0, 0}, {"/mod", w_slash_mod, 0, 0}, {"0<", w_zero_less, 0, 0}, {"0=", w_zero_equals, 0, 0}, {"0<>", w_zero_not_equals, 0, 0}, {"0>", w_zero_greater, 0, 0}, {"<", w_less, 0, 0}, {"=", w_equals, 0, 0}, {"<>", w_not_equals, 0, 0}, {">", w_greater, 0, 0}, {"1+", wp_add, 0, 1}, {"char+", wp_add, 0, 1}, {"1-", wp_add, 0, -1}, {"2+", wp_add, 0, 2}, {"2-", wp_add, 0, -2}, {"2*", wp_mul, 0, 2}, {"chars", wp_mul, 0, 1}, {"2/", wp_div, 0, 2}, {"cells", wp_mul, 0, CELL}, {"cell+", wp_add, 0, CELL}, /* Stack manip */ {"drop", w_drop, 0, 0}, {"dup", w_dupe, 0, 0}, {"?dup", w_question_dupe, 0, 0}, {"over", w_over, 0, 0}, {"swap", w_swap, 0, 0}, {"rot", w_rot, 0, 0}, {"tuck", w_tuck, 0, 0}, {"pick", w_pick, 0, 0}, {"roll", w_roll, 0, 0}, /* Double wide stack manip */ {"2drop", w_two_drop, 0, 0}, {"2dup", w_two_dup, 0, 0}, {"2over", w_two_over, 0, 0}, {"2swap", w_two_swap, 0, 0}, // /* Return stack manip */ {">r", w_to_r, 0, 0}, {"r>", w_r_from, 0, 0}, {"r@", w_r_fetch, 0, 0}, // /* Double wide return stack manip */ {"2>r", w_two_to_r, 0, 0}, {"2r>", w_two_r_from, 0, 0}, {"2r@", w_two_r_fetch, 0, 0}, /* Printing */ {".", w_dot, 0, 0}, {"type", w_type, 0, 0}, {"cr", wp_putc, 0, '\n'}, {"space", wp_putc, 0, ' '}, {"bl", wp_const, 0, ' '}, {"dump", w_dump, 0, 0}, {"emit", w_emit, 0, 0}, /* Control flow */ {"abort", w_abort, 0, 0}, {"quit", w_quit, 0, 0}, {"exit", w_exit, 0, 0}, /* Syntax */ {":", w_colon, 0, 0}, {";", w_semicolon, 1, 0}, {"\\", w_backslash, 1, 0}, // line comment {"(", w_paren, 1, 0}, // enclosed comment { /* end marker */ } }; // foreach struct fh_word_s w; const struct name_and_handler *p = builtins; enum fh_error rv; while (p->handler) { strcpy(w.name, p->name); w.handler = p->handler; w.builtin = 1; w.immediate = p->immediate; w.param = p->param; rv = fh_add_word(&w, fh); if (rv != FH_OK) { return rv; } p++; } return FH_OK; }