many fixes and add some missing words

master
Ondřej Hruška 3 years ago
parent dd12f8170a
commit e67d85d64e
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 16
      README.md
  2. 13
      include/fh_config.h
  3. 2
      include/fh_error.h
  4. 36
      include/fh_helpers.h
  5. 9
      include/fh_parse.h
  6. 5
      include/fh_print.h
  7. 50
      include/fh_runtime.h
  8. 3
      include/fh_stack.h
  9. 8
      include/forth.h
  10. 31
      include/forth_internal.h
  11. 6
      src/fh_builtins.c
  12. 244
      src/fh_builtins_arith.c
  13. 30
      src/fh_builtins_control.c
  14. 51
      src/fh_builtins_mem.c
  15. 50
      src/fh_builtins_meta.c
  16. 5
      src/fh_builtins_stack.c
  17. 21
      src/fh_builtins_system.c
  18. 18
      src/fh_builtins_text.c
  19. 4
      src/fh_error.c
  20. 28
      src/fh_mem.c
  21. 192
      src/fh_parse.c
  22. 202
      src/fh_runtime.c
  23. 50
      src/fh_see.c
  24. 24
      src/fh_stack.c
  25. 6
      src/main.c
  26. 1845
      testfiles/combinedtest.f
  27. 33
      testfiles/ifmacro.f

@ -44,15 +44,15 @@ Implementation status
*(this section may be outdated)* *(this section may be outdated)*
Implemented: Implemented (some may be wrong, like `FM/MOD`):
``` ```
CORE: CORE:
! ' ( * */ */MOD + +! +LOOP , - . ." / /mod 0< 0= 1+ 1- 2! 2* 2/ 2@ 2DROP 2DUP 2OVER 2SWAP ! ' ( * */ */MOD + +! +LOOP , - . ." / /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 CELL CELL+ CELLS CHAR CHAR+ : ; < = > >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 DROP DUP ELSE EMIT ENVIRONMENT? EXECUTE EXIT FIND CHARS CONSTANT COUNT CR CREATE DECIMAL DEPTH DO DROP DUP ELSE EMIT ENVIRONMENT? EXECUTE EXIT FM/MOD FIND
HERE I IF IMMEDIATE INVERT J LEAVE LITERAL LOOP MOD NEGATE OR OVER POSTPONE QUIT R> R@ RECURSE HERE I IF IMMEDIATE INVERT J LEAVE LITERAL LOOP LSHIFT M* MAX MIN MOD NEGATE OR OVER POSTPONE QUIT R> R@ RECURSE
REPEAT ROT S" SOURCE SPACE SWAP THEN TYPE U< UNTIL VARIABLE WHILE WORD XOR [ ['] [CHAR] ] REPEAT ROT RSHIFT S>D S" SM/REM SOURCE SPACE STATE SWAP THEN TYPE U< UNTIL UM* UM/MOD UNLOOP VARIABLE WHILE WORD XOR [ ['] [CHAR] ]
CORE-EXT: CORE-EXT:
.( 0<> 0> 2>R 2R> 2R@ <> ?DO AGAIN FALSE HEX NIP PAD PICK ROLL S\" TO TRUE TUCK U.R U> UNUSED VALUE .( 0<> 0> 2>R 2R> 2R@ <> ?DO AGAIN FALSE HEX NIP PAD PICK ROLL S\" TO TRUE TUCK U.R U> UNUSED VALUE
@ -64,13 +64,11 @@ FORGET SEE BYE
Missing: Missing:
basically, CASE-OF, pictured numbers, some specialty math stuff, working with characters, the more basically, CASE-OF, pictured numbers, keyboard input, some weirder metaprogramming things, manipulating the input buffer.
weird metaprogramming things, and manipulating the input buffer.
``` ```
CORE: CORE:
# #> #S <# >BODY >NUMBER ABORT" ACCEPT C! C, C@ DOES> EVALUATE FILL FM/MOD HOLD KEY LSHIFY M* MAX # #> #S <# >BODY >NUMBER ABORT" ACCEPT DOES> EVALUATE FILL HOLD KEY MOVE SIGN SPACES U.
MIN MOVE RSHIFT S>D SIGN SM/REM SPACES STATE U. UM* UM/MOD UNLOOP
CORE-EXT: CORE-EXT:
.R :NONAME ACTION-OF BUFFER: C" CASE COMPILE, DEFER DEFER! DEFER@ ENDCASE ENDOF ERASE HOLDS IS .R :NONAME ACTION-OF BUFFER: C" CASE COMPILE, DEFER DEFER! DEFER@ ENDCASE ENDOF ERASE HOLDS IS

@ -19,4 +19,17 @@
#define CELL 4 #define CELL 4
#define HEAP_END (HEAP_SIZE - WORDBUF_SIZE - INPUT_BUFFER_SIZE)
#define WORDBUF_ADDR HEAP_END
#define INPUTBUF_ADDR (HEAP_END + WORDBUF_SIZE)
// SFR and magic addresses are "negative"
#define MAGICADDR_EXEC_INTERACTIVE 0xFFFFFFFFULL
#define MAGICADDR_DICTFIRST 0xFFFFd1c7ULL
#define MAGICADDR_BASE 0xFFFFBA5EULL
#define MAGICADDR_HERE 0xFFFF4E7EULL
#define MAGICADDR_STATE 0xFFFF57a7ULL
#define MAGICADDR_INPTR 0xFFFFF175ULL
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
#endif //FORTH_FH_CONFIG_H #endif //FORTH_FH_CONFIG_H

@ -25,7 +25,7 @@ enum fh_error {
FH_ERR_UNKNOWN_WORD, FH_ERR_UNKNOWN_WORD,
FH_ERR_ILLEGAL_FETCH, FH_ERR_ILLEGAL_FETCH,
FH_ERR_ILLEGAL_STORE, FH_ERR_ILLEGAL_STORE,
FH_ERR_DIV_BY_ZERO, FH_ERR_ARITH,
FH_ERR_SYNTAX, FH_ERR_SYNTAX,
FH_ERR_NOT_APPLICABLE, FH_ERR_NOT_APPLICABLE,
FH_ERR_MAX, FH_ERR_MAX,

@ -0,0 +1,36 @@
/**
* Helper macros
*
* Created on 2021/11/17.
*/
#ifndef FORTH_FH_MACROS_H
#define FORTH_FH_MACROS_H
/**
* strncasecmp with guard against prefix match
*
* a - input string
* b - example string with terminator
* n - len of input string
*/
#define EQ(a, b, n) (0 == strncasecmp((a), (b), (n)) && (b)[(n)]==0)
/** Get a value rounded up to multiple of word size */
#define WORDALIGNED(var) (((var) + 3) & ~3)
_Static_assert(WORDALIGNED(0) == 0, "word align");
_Static_assert(WORDALIGNED(1) == 4, "word align");
_Static_assert(WORDALIGNED(2) == 4, "word align");
_Static_assert(WORDALIGNED(3) == 4, "word align");
_Static_assert(WORDALIGNED(4) == 4, "word align");
_Static_assert(WORDALIGNED(5) == 8, "word align");
_Static_assert(WORDALIGNED(1023) == 1024, "word align");
_Static_assert(WORDALIGNED(1024) == 1024, "word align");
#define TRY(x) \
do { \
if (FH_OK != (rv = (x))) return rv; \
} while (0)
#endif //FORTH_FH_MACROS_H

@ -7,15 +7,18 @@
#ifndef FORTH_FH_PARSE_H #ifndef FORTH_FH_PARSE_H
#define FORTH_FH_PARSE_H #define FORTH_FH_PARSE_H
typedef bool (*chartest_t)(char c, void* param); typedef bool (*chartest_t)(char c, void *param);
void fh_input_consume_matching(struct fh_thread_s *fh, chartest_t test, void* param); void fh_input_consume_matching(struct fh_thread_s *fh, chartest_t test, void *param);
void fh_input_consume_spaces(struct fh_thread_s *fh); void fh_input_consume_spaces(struct fh_thread_s *fh);
enum fh_error fh_input_read_delimited(struct fh_thread_s *fh, char **out, size_t *len, chartest_t test, void* param); enum fh_error fh_input_read_delimited(struct fh_thread_s *fh, char **out, size_t *len, chartest_t test, void *param);
enum fh_error fh_input_read_word(struct fh_thread_s *fh, char **out, size_t *len); enum fh_error fh_input_read_word(struct fh_thread_s *fh, char **out, size_t *len);
enum fh_error fh_input_read_quotedstring(struct fh_thread_s *fh, bool escaped, char *outbuf, size_t capacity, size_t *out_len); enum fh_error fh_input_read_quotedstring(struct fh_thread_s *fh, bool escaped, char *outbuf, size_t capacity, size_t *out_len);
enum fh_error fh_handle_ascii_word(struct fh_thread_s *fh, const char *name, size_t wordlen);
#endif //FORTH_FH_PARSE_H #endif //FORTH_FH_PARSE_H

@ -7,11 +7,6 @@
#ifndef FORTH_FH_PRINT_H #ifndef FORTH_FH_PRINT_H
#define FORTH_FH_PRINT_H #define FORTH_FH_PRINT_H
/* for printing */
#include <stdlib.h>
#include <stdio.h>
#include "fh_globals.h"
/* logging */ /* logging */
#define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\n", ##__VA_ARGS__); } } while (0) #define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\n", ##__VA_ARGS__); } } while (0)
#define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\n", ##__VA_ARGS__) #define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\n", ##__VA_ARGS__)

@ -78,12 +78,6 @@ struct fh_instruction_s {
uint32_t data; uint32_t data;
}; };
static inline void instr_init(struct fh_instruction_s *instr, enum fh_instruction_kind kind, uint32_t data)
{
instr->kind = kind;
instr->data = data;
}
#define INSTR_SIZE (sizeof(struct fh_instruction_s)) #define INSTR_SIZE (sizeof(struct fh_instruction_s))
_Static_assert(sizeof(struct fh_instruction_s) % 4 == 0, "Instruction struct is aligned"); _Static_assert(sizeof(struct fh_instruction_s) % 4 == 0, "Instruction struct is aligned");
@ -107,9 +101,12 @@ enum fh_substate {
FH_SUBSTATE_PAREN_COMMENT, FH_SUBSTATE_PAREN_COMMENT,
FH_SUBSTATE_LINE_COMMENT, FH_SUBSTATE_LINE_COMMENT,
FH_SUBSTATE_EXIT, FH_SUBSTATE_EXIT,
FH_SUBSTATE_SKIP_IF,
FH_SUBSTATE_MAX, FH_SUBSTATE_MAX,
}; };
extern const char *substatenames[FH_SUBSTATE_MAX];
/** Marks a dictionary entry that is a word */ /** Marks a dictionary entry that is a word */
#define WORDFLAG_WORD 0x01 #define WORDFLAG_WORD 0x01
/** Indicates that this is a built-in instruction and not a word call */ /** Indicates that this is a built-in instruction and not a word call */
@ -147,11 +144,8 @@ struct fh_word_s {
/** Word name */ /** Word name */
char name[MAX_NAME_LEN]; // XXX this wastes RAM! char name[MAX_NAME_LEN]; // XXX this wastes RAM!
}; };
#define MAGICADDR_DICTFIRST 0xFFFFFFFFULL
#define DICTWORD_SIZE sizeof(struct fh_word_s) #define DICTWORD_SIZE sizeof(struct fh_word_s)
/** /**
@ -201,11 +195,10 @@ struct fh_thread_s {
/** Loop variable J */ /** Loop variable J */
uint32_t loop_j; uint32_t loop_j;
};
#define HEAP_END (HEAP_SIZE - WORDBUF_SIZE - INPUT_BUFFER_SIZE) /** Nesting level of [if] */
#define WORDBUF_ADDR HEAP_END uint32_t parse_if_level;
#define INPUTBUF_ADDR (HEAP_END + WORDBUF_SIZE) };
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);
@ -232,33 +225,6 @@ enum fh_error fh_see_word(
size_t wordlen size_t wordlen
); );
/* if the return address is this, we should drop back to interactive mode */
// SFR and magic addresses are "negative"
#define MAGICADDR_EXEC_INTERACTIVE 0xFFFFFFFFULL
#define MAGICADDR_BASE 0xFFFFBA5EULL
#define MAGICADDR_HERE 0xFFFF4E7EULL
#define MAGICADDR_INPTR 0xFFFFF111ULL
#define MAGICADDR_UNRESOLVED 0xFFFFFBADULL
/** Get a value rounded up to multiple of word size */
#define WORDALIGNED(var) (((var) + 3) & ~3)
_Static_assert(WORDALIGNED(0) == 0, "word align");
_Static_assert(WORDALIGNED(1) == 4, "word align");
_Static_assert(WORDALIGNED(2) == 4, "word align");
_Static_assert(WORDALIGNED(3) == 4, "word align");
_Static_assert(WORDALIGNED(4) == 4, "word align");
_Static_assert(WORDALIGNED(5) == 8, "word align");
_Static_assert(WORDALIGNED(1023) == 1024, "word align");
_Static_assert(WORDALIGNED(1024) == 1024, "word align");
#define TRY(x) \
do { \
if (FH_OK != (rv = (x))) return rv; \
} while (0)
/** /**
* Execute a dictionary word from a definition stored at the given address * Execute a dictionary word from a definition stored at the given address
* @param fh * @param fh
@ -278,4 +244,8 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr);
*/ */
enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out); enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out);
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);
#endif //FORTH_FH_RUNTIME_H #endif //FORTH_FH_RUNTIME_H

@ -18,6 +18,9 @@ static inline enum fh_error cs_peek_n(struct fh_thread_s *fh, uint32_t *out, int
return ds_peek_n(fh, out, n); return ds_peek_n(fh, out, n);
} }
enum fh_error ds_push_dw(struct fh_thread_s *fh, uint64_t in);
enum fh_error ds_pop_dw(struct fh_thread_s *fh, uint64_t *out);
enum fh_error rs_poke_n(struct fh_thread_s *fh, uint32_t value, int n); enum fh_error rs_poke_n(struct fh_thread_s *fh, uint32_t value, int n);
/** Peek top of data stack */ /** Peek top of data stack */

@ -13,10 +13,8 @@
#include "fh_config.h" #include "fh_config.h"
#include "fh_error.h" #include "fh_error.h"
#include "fh_globals.h"
struct fh_thread_s; #include "fh_runtime.h"
#include "fh_print.h"
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);
#endif //FORTH_H #endif //FORTH_H

@ -0,0 +1,31 @@
/**
* TODO file description
*
* Created on 2021/11/17.
*/
#ifndef FORTH_FORTH_INTERNAL_H
#define FORTH_FORTH_INTERNAL_H
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include "fh_config.h"
#include "fh_error.h"
#include "fh_helpers.h"
#include "fh_globals.h"
#include "fh_print.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_parse.h"
#include "fh_builtins.h"
#endif //FORTH_FORTH_INTERNAL_H

@ -1,8 +1,4 @@
#include <string.h> #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_error.h"
#include "fh_print.h"
#include "fh_builtins.h"
enum fh_error fh_register_words_from_array(struct fh_thread_s *fh, const struct name_and_handler *p) enum fh_error fh_register_words_from_array(struct fh_thread_s *fh, const struct name_and_handler *p)
{ {

@ -1,9 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error wp_setbase(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
@ -84,13 +79,35 @@ static enum fh_error w_xor(struct fh_thread_s *fh, const struct fh_word_s *w)
return FH_OK; return FH_OK;
} }
static enum fh_error w_lshift(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, a << b));
return FH_OK;
}
static enum fh_error w_rshift(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, a >> b));
return FH_OK;
}
static enum fh_error w_zero_less(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_zero_less(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
uint32_t a = 0; uint32_t a = 0;
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
TRY(ds_push(fh, TOBOOL((int32_t)a < 0))); TRY(ds_push(fh, TOBOOL((int32_t) a < 0)));
return FH_OK; return FH_OK;
} }
@ -124,6 +141,27 @@ static enum fh_error w_zero_not_equals(struct fh_thread_s *fh, const struct fh_w
return FH_OK; return FH_OK;
} }
static enum fh_error w_min(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, (int32_t) a < (int32_t) b ? a : b));
return FH_OK;
}
static enum fh_error w_max(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, (int32_t) a < (int32_t) b ? b : a));
return FH_OK;
}
static enum fh_error w_less(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_less(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
@ -215,7 +253,17 @@ static enum fh_error wp_div(struct fh_thread_s *fh, const struct fh_word_s *w)
enum fh_error rv; enum fh_error rv;
uint32_t a = 0; uint32_t a = 0;
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
TRY(ds_push(fh, a * w->param)); TRY(ds_push(fh, (int32_t) a / (int32_t) w->param));
return FH_OK;
}
static enum fh_error w_2div(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, (a & 0x80000000) | (a >> 1)));
return FH_OK; return FH_OK;
} }
@ -229,10 +277,10 @@ static enum fh_error w_star_slash(struct fh_thread_s *fh, const struct fh_word_s
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
if (c == 0) { if (c == 0) {
return FH_ERR_DIV_BY_ZERO; return FH_ERR_ARITH;
} }
uint64_t v = ((uint64_t) a * (uint64_t) b) / (uint64_t) c; int64_t v = ((int64_t) (int32_t)a * (int64_t) (int32_t)b) / (int64_t) (int32_t)c;
TRY(ds_push(fh, (uint32_t) v)); TRY(ds_push(fh, (uint32_t) v));
return FH_OK; return FH_OK;
@ -248,15 +296,15 @@ static enum fh_error w_star_slash_mod(struct fh_thread_s *fh, const struct fh_wo
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
if (c == 0) { if (c == 0) {
return FH_ERR_DIV_BY_ZERO; return FH_ERR_ARITH;
} }
uint64_t product = ((uint64_t) a * (uint64_t) b); int64_t product = ((int64_t) (int32_t)a * (int64_t) (int32_t)b);
uint64_t v = product / (uint64_t) c; int64_t v = product / (int64_t) (int32_t)c;
uint64_t m = product % (uint64_t) c; int64_t m = product % (int64_t) (int32_t)c;
TRY(ds_push(fh, (uint32_t) m)); TRY(ds_push(fh, (uint32_t) (int32_t)m));
TRY(ds_push(fh, (uint32_t) v)); TRY(ds_push(fh, (uint32_t) (int32_t)v));
return FH_OK; return FH_OK;
} }
@ -269,10 +317,10 @@ static enum fh_error w_slash(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
if (b == 0) { if (b == 0) {
return FH_ERR_DIV_BY_ZERO; return FH_ERR_ARITH;
} }
TRY(ds_push(fh, a / b)); TRY(ds_push(fh, (int32_t)a / (int32_t)b));
return FH_OK; return FH_OK;
} }
@ -320,11 +368,11 @@ static enum fh_error w_slash_mod(struct fh_thread_s *fh, const struct fh_word_s
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
if (b == 0) { if (b == 0) {
return FH_ERR_DIV_BY_ZERO; return FH_ERR_ARITH;
} }
uint32_t rem = a % b; int32_t rem = (int32_t)a % (int32_t)b;
uint32_t div = a / b; int32_t div = (int32_t)a / (int32_t)b;
TRY(ds_push(fh, rem)); TRY(ds_push(fh, rem));
TRY(ds_push(fh, div)); TRY(ds_push(fh, div));
@ -340,15 +388,153 @@ static enum fh_error w_mod(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(ds_pop(fh, &a)); TRY(ds_pop(fh, &a));
if (b == 0) { if (b == 0) {
return FH_ERR_DIV_BY_ZERO; return FH_ERR_ARITH;
} }
uint32_t rem = a % b; int32_t rem = (int32_t)a % (int32_t)b;
TRY(ds_push(fh, rem)); TRY(ds_push(fh, rem));
return FH_OK; return FH_OK;
} }
static enum fh_error w_s_to_d(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));
int32_t as = (int32_t) a; // because of sign extend
int64_t a64 = as;
TRY(ds_push_dw(fh, (uint64_t) a64));
return FH_OK;
}
static enum fh_error w_m_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));
// make signed and then sign extend
int64_t res = (int64_t) (int32_t) a * (int64_t) (int32_t) b;
TRY(ds_push_dw(fh, (uint64_t) res));
return FH_OK;
}
static enum fh_error w_um_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));
// make signed and then sign extend
uint64_t res = (uint64_t) a * (uint64_t) b;
TRY(ds_push_dw(fh, res));
return FH_OK;
}
// Copied from https://stackoverflow.com/a/51457071/2180189
void floor_div64(int64_t *q, int64_t *r, int64_t a, int64_t b)
{
int64_t q0 = a / b;
int64_t r0 = a % b;
if (b > 0){
*q = r0 >= 0 ? q0 : q0 - 1;
*r = r0 >= 0 ? r0 : r0 + b;
}
else {
*q = r0 <= 0 ? q0 : q0 - 1;
*r = r0 <= 0 ? r0 : r0 + b;
}
}
static enum fh_error w_fm_mod(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
int32_t div;
int64_t num;
TRY(ds_pop(fh, (uint32_t*)&div));
TRY(ds_pop_dw(fh, (uint64_t*)&num));
int64_t res, rem;
floor_div64(&res, &rem, num, div);
if ((int64_t)(int32_t)rem != rem) {
LOGE("Remainder too large");
return FH_ERR_ARITH;
}
if ((int64_t)(int32_t)res != res) {
LOGE("Division result too large");
return FH_ERR_ARITH;
}
TRY(ds_push(fh, (int32_t)rem));
TRY(ds_push(fh, (int32_t)res));
return FH_OK;
}
static enum fh_error w_um_mod(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
uint32_t div;
uint64_t num;
TRY(ds_pop(fh, &div));
TRY(ds_pop_dw(fh, &num));
uint64_t res = num / (uint64_t)div; // TODO verify this may be wrong
uint64_t rem = num % (uint64_t)div;
if ((uint64_t)(uint32_t)rem != rem) {
LOGE("Remainder too large");
return FH_ERR_ARITH;
}
if ((uint64_t)(uint32_t)res != res) {
LOGE("Division result too large");
return FH_ERR_ARITH;
}
TRY(ds_push(fh, (uint32_t)rem));
TRY(ds_push(fh, (uint32_t)res));
return FH_OK;
}
static enum fh_error w_sm_rem(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
int32_t div;
int64_t num;
TRY(ds_pop(fh, (uint32_t*)&div));
TRY(ds_pop_dw(fh, (uint64_t*)&num));
int64_t res = num / (int64_t)div;
int64_t rem = num % (int64_t)div;
if ((int64_t)(int32_t)rem != rem) {
LOGE("Remainder too large");
return FH_ERR_ARITH;
}
if ((int64_t)(int32_t)res != res) {
LOGE("Division result too large");
return FH_ERR_ARITH;
}
TRY(ds_push(fh, (int32_t)rem));
TRY(ds_push(fh, (int32_t)res));
return FH_OK;
}
const struct name_and_handler fh_builtins_arith[] = { const struct name_and_handler fh_builtins_arith[] = {
/* Arithmetics */ /* Arithmetics */
{"base", wp_const, 0, MAGICADDR_BASE}, {"base", wp_const, 0, MAGICADDR_BASE},
@ -370,6 +556,10 @@ const struct name_and_handler fh_builtins_arith[] = {
{"mod", w_mod, 0, 0}, {"mod", w_mod, 0, 0},
{"invert", w_invert, 0, 0}, {"invert", w_invert, 0, 0},
{"negate", w_negate, 0, 0}, {"negate", w_negate, 0, 0},
{"lshift", w_lshift, 0, 0},
{"rshift", w_rshift, 0, 0},
{"min", w_min, 0, 0},
{"max", w_max, 0, 0},
{"0<", w_zero_less, 0, 0}, {"0<", w_zero_less, 0, 0},
{"0=", w_zero_equals, 0, 0}, {"0=", w_zero_equals, 0, 0},
{"0<>", w_zero_not_equals, 0, 0}, {"0<>", w_zero_not_equals, 0, 0},
@ -385,6 +575,12 @@ const struct name_and_handler fh_builtins_arith[] = {
{"2+", wp_add, 0, 2}, {"2+", wp_add, 0, 2},
{"2-", wp_add, 0, -2}, {"2-", wp_add, 0, -2},
{"2*", wp_mul, 0, 2}, {"2*", wp_mul, 0, 2},
{"2/", wp_div, 0, 2}, {"2/", w_2div, 0, 0},
{"s>d", w_s_to_d, 0, 0},
{"m*", w_m_star, 0, 0},
{"um*", w_um_star, 0, 0},
{"fm/mod", w_fm_mod, 0, 0},
{"sm/rem", w_sm_rem, 0, 0},
{"um/mod", w_um_mod, 0, 0},
{ /* end marker */ } { /* end marker */ }
}; };

@ -1,9 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
static enum fh_error w_recurse(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_recurse(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
@ -36,7 +31,7 @@ static enum fh_error w_else(struct fh_thread_s *fh, const struct fh_word_s *w)
uint32_t ifaddr = 0; uint32_t ifaddr = 0;
TRY(cs_pop(fh, &ifaddr)); TRY(cs_pop(fh, &ifaddr));
struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr); struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr);
if (if_instr->data != MAGICADDR_UNRESOLVED) { if (!if_instr || if_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("IF-ELSE control stack corruption"); LOGE("IF-ELSE control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
@ -57,7 +52,7 @@ static enum fh_error w_then(struct fh_thread_s *fh, const struct fh_word_s *w)
uint32_t ifaddr = 0; uint32_t ifaddr = 0;
TRY(cs_pop(fh, &ifaddr)); TRY(cs_pop(fh, &ifaddr));
struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr); struct fh_instruction_s *if_instr = fh_instr_at(fh, ifaddr);
if (if_instr->data != MAGICADDR_UNRESOLVED) { if (!if_instr || if_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("IF-ELSE control stack corruption"); LOGE("IF-ELSE control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
@ -119,12 +114,16 @@ static enum fh_error wp_loop(struct fh_thread_s *fh, const struct fh_word_s *w)
// resolve ?DO dest // resolve ?DO dest
ii = fh_instr_at(fh, startaddr - INSTR_SIZE); ii = fh_instr_at(fh, startaddr - INSTR_SIZE);
if (ii->kind == FH_INSTR_DO_QUESTION && ii->data == MAGICADDR_UNRESOLVED) { if (!ii || ii->kind == FH_INSTR_DO_QUESTION && ii->data == MAGICADDR_UNRESOLVED) {
ii->data = endaddr; ii->data = endaddr;
} }
while (startaddr < loopendaddr) { while (startaddr < loopendaddr) {
ii = fh_instr_at(fh, startaddr); ii = fh_instr_at(fh, startaddr);
if (!ii) {
LOGE("WHAT?");
return FH_ERR_INTERNAL;
}
if (ii->kind == FH_INSTR_LEAVE && ii->data == MAGICADDR_UNRESOLVED) { if (ii->kind == FH_INSTR_LEAVE && ii->data == MAGICADDR_UNRESOLVED) {
LOG("Resolve leave addr"); LOG("Resolve leave addr");
ii->data = endaddr; ii->data = endaddr;
@ -142,6 +141,16 @@ static enum fh_error wp_loop(struct fh_thread_s *fh, const struct fh_word_s *w)
return FH_OK; return FH_OK;
} }
static enum fh_error w_unloop(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
uint32_t limit;
TRY(rs_pop(fh, &limit));
TRY(fh_loop_unnest(fh));
return FH_OK;
}
static enum fh_error w_leave(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_leave(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
@ -182,7 +191,7 @@ static enum fh_error w_repeat(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(cs_pop(fh, &origaddr)); TRY(cs_pop(fh, &origaddr));
struct fh_instruction_s *branch_instr = fh_instr_at(fh, origaddr); struct fh_instruction_s *branch_instr = fh_instr_at(fh, origaddr);
if (branch_instr->data != MAGICADDR_UNRESOLVED) { if (!branch_instr || branch_instr->data != MAGICADDR_UNRESOLVED) {
LOGE("REPEAT control stack corruption"); LOGE("REPEAT control stack corruption");
return FH_ERR_INTERNAL; return FH_ERR_INTERNAL;
} }
@ -231,6 +240,7 @@ const struct name_and_handler fh_builtins_control[] = {
{"repeat", w_repeat, 1, 0}, {"repeat", w_repeat, 1, 0},
{"again", w_again, 1, 0}, {"again", w_again, 1, 0},
{"until", w_until, 1, 0}, {"until", w_until, 1, 0},
{"unloop", w_unloop, 0, 0},
{ /* end marker */ } { /* end marker */ }
}; };

@ -1,9 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
static enum fh_error w_fetch(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_fetch(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
@ -117,6 +112,46 @@ static enum fh_error w_comma(struct fh_thread_s *fh, const struct fh_word_s *w)
return FH_OK; return FH_OK;
} }
static enum fh_error w_c_comma(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
uint32_t value = 0;
TRY(ds_pop(fh, &value));
TRY(fh_heap_put(fh, &value, 1));
return FH_OK;
}
static enum fh_error w_c_store(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
uint32_t addr;
TRY(ds_pop(fh, &addr));
uint32_t value = 0;
TRY(ds_pop(fh, &value));
TRY(fh_store_char(fh, addr, (char) value));
return FH_OK;
}
static enum fh_error w_c_fetch(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
uint32_t addr;
TRY(ds_pop(fh, &addr));
char value = 0;
TRY(fh_fetch_char(fh, addr, &value));
TRY(ds_push(fh, (uint32_t) value));
return FH_OK;
}
static enum fh_error w_align(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_align(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
@ -161,7 +196,11 @@ const struct name_and_handler fh_builtins_mem[] = {
{"allot", w_allot, 0, 0}, {"allot", w_allot, 0, 0},
{"align", w_align, 0, 0}, {"align", w_align, 0, 0},
{",", w_comma, 0, 0}, {",", w_comma, 0, 0},
{"c,", w_c_comma, 0, 0},
{"c@", w_c_fetch, 0, 0},
{"c!", w_c_store, 0, 0},
{"here", w_here, 0, 0}, {"here", w_here, 0, 0},
{"state", wp_const, 0, MAGICADDR_STATE},
{"pad", w_pad, 0, 0}, {"pad", w_pad, 0, 0},
{ /* end marker */ } { /* end marker */ }

@ -1,11 +1,4 @@
#include <string.h> #include "forth_internal.h"
#include "fh_error.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
#include "fh_parse.h"
static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
@ -25,6 +18,7 @@ static enum fh_error w_colon(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr)); TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr));
struct fh_word_s *new_word = fh_word_at(fh, ptr); struct fh_word_s *new_word = fh_word_at(fh, ptr);
if (!new_word) return FH_ERR_INTERNAL;
new_word->previous = fh->dict_last; new_word->previous = fh->dict_last;
new_word->param = fh->here; new_word->param = fh->here;
new_word->handler = w_user_word; new_word->handler = w_user_word;
@ -53,6 +47,7 @@ static enum fh_error w_forget(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(fh_find_word(fh, wordname, namelen, &addr)); TRY(fh_find_word(fh, wordname, namelen, &addr));
struct fh_word_s *removedword = fh_word_at(fh, addr); struct fh_word_s *removedword = fh_word_at(fh, addr);
if (!removedword) return FH_ERR_INTERNAL;
fh->dict_last = removedword->previous; fh->dict_last = removedword->previous;
return FH_OK; return FH_OK;
} }
@ -91,6 +86,16 @@ static enum fh_error wp_variable(struct fh_thread_s *fh, const struct fh_word_s
{ {
(void) w; (void) w;
enum fh_error rv; enum fh_error rv;
bool is_value = w->param == 1;
bool is_const = w->param == 2;
if (is_const && fh->state == FH_STATE_COMPILE) {
uint32_t wordaddr = (void *)w - (void *)&fh->heap[0]; // this is ugly
TRY(fh_put_instr(fh, FH_INSTR_WORD, wordaddr));
return FH_OK;
}
ENSURE_STATE(FH_STATE_INTERPRET); ENSURE_STATE(FH_STATE_INTERPRET);
char *wordname; char *wordname;
@ -101,9 +106,6 @@ static enum fh_error wp_variable(struct fh_thread_s *fh, const struct fh_word_s
uint32_t ptr; uint32_t ptr;
uint32_t value = 0; uint32_t value = 0;
bool is_value = w->param == 1;
bool is_const = w->param == 2;
if (is_value || is_const) { if (is_value || is_const) {
TRY(ds_pop(fh, &value)); TRY(ds_pop(fh, &value));
} }
@ -111,6 +113,7 @@ static enum fh_error wp_variable(struct fh_thread_s *fh, const struct fh_word_s
TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr)); TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr));
struct fh_word_s *new_word = fh_word_at(fh, ptr); struct fh_word_s *new_word = fh_word_at(fh, ptr);
if (!new_word) return FH_ERR_INTERNAL;
new_word->previous = fh->dict_last; new_word->previous = fh->dict_last;
new_word->param = value; new_word->param = value;
new_word->handler = (is_value || is_const) ? rt_read_value : rt_read_varaddr; new_word->handler = (is_value || is_const) ? rt_read_value : rt_read_varaddr;
@ -147,10 +150,16 @@ static enum fh_error w_to(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(fh_find_word(fh, wordname, namelen, &waddr)); TRY(fh_find_word(fh, wordname, namelen, &waddr));
struct fh_word_s *ww = fh_word_at(fh, waddr); struct fh_word_s *ww = fh_word_at(fh, waddr);
if (!ww) return FH_ERR_INTERNAL;
if (ww->flags & WORDFLAG_WORD) {
LOGE("Cannot assign to dictionary word param field!");
return FH_ERR_NOT_APPLICABLE;
}
if (ww->flags & WORDFLAG_CONSTANT) { if (ww->flags & WORDFLAG_CONSTANT) {
LOGE("Cannot assign to constant!"); LOGE("Cannot assign to constant!");
return FH_ERR_ILLEGAL_STORE; return FH_ERR_NOT_APPLICABLE;
} }
ww->param = value; ww->param = value;
@ -236,7 +245,9 @@ static enum fh_error w_immediate(struct fh_thread_s *fh, const struct fh_word_s
return FH_ERR_INVALID_STATE; return FH_ERR_INVALID_STATE;
} }
fh_word_at(fh, fh->dict_last)->flags |= WORDFLAG_IMMEDIATE; struct fh_word_s *word = fh_word_at(fh, fh->dict_last);
if (!word) return FH_ERR_INTERNAL;
word->flags |= WORDFLAG_IMMEDIATE;
return FH_OK; return FH_OK;
} }
@ -365,6 +376,7 @@ static enum fh_error w_create(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr)); TRY(fh_heap_reserve(fh, DICTWORD_SIZE, &ptr));
struct fh_word_s *new_word = fh_word_at(fh, ptr); struct fh_word_s *new_word = fh_word_at(fh, ptr);
if (!new_word) return FH_ERR_INTERNAL;
new_word->previous = fh->dict_last; new_word->previous = fh->dict_last;
new_word->param = fh->here; new_word->param = fh->here;
new_word->handler = rt_read_value; new_word->handler = rt_read_value;
@ -398,6 +410,7 @@ static enum fh_error w_find(struct fh_thread_s *fh, const struct fh_word_s *w)
} }
struct fh_word_s *word = fh_word_at(fh, addr); struct fh_word_s *word = fh_word_at(fh, addr);
if (!word) return FH_ERR_INTERNAL;
TRY(ds_push(fh, addr)); TRY(ds_push(fh, addr));
TRY(ds_push(fh, (word->flags & WORDFLAG_IMMEDIATE) ? 1 : -1)); TRY(ds_push(fh, (word->flags & WORDFLAG_IMMEDIATE) ? 1 : -1));
@ -445,6 +458,10 @@ static enum fh_error w_execute(struct fh_thread_s *fh, const struct fh_word_s *w
} }
struct fh_word_s *word = fh_word_at(fh, addr); struct fh_word_s *word = fh_word_at(fh, addr);
if (!word) {
LOGE("Execute with bad addr");
return FH_ERR_NOT_APPLICABLE;
}
if (!word->handler) { if (!word->handler) {
LOGE("Execute word with no handler"); LOGE("Execute word with no handler");
return FH_ERR_NOT_APPLICABLE; return FH_ERR_NOT_APPLICABLE;
@ -463,8 +480,12 @@ static enum fh_error w_env_query(struct fh_thread_s *fh, const struct fh_word_s
uint32_t addr; uint32_t addr;
TRY(ds_pop(fh, &addr)); TRY(ds_pop(fh, &addr));
const char *str = fh_str_at(fh, addr); const char *str = fh_str_at(fh, addr);
if (!str) {
LOGE("Bad string addr for env query!");
return FH_ERR_NOT_APPLICABLE;
}
#define EQ(a, b, n) (0 == strncasecmp((a), (b), n) && (b)[n]==0) LOG("Test environment \"%.*s\"", len, str);
if (EQ(str, "/COUNTED-STRING", len)) { if (EQ(str, "/COUNTED-STRING", len)) {
TRY(ds_push(fh, 255)); TRY(ds_push(fh, 255));
@ -522,7 +543,6 @@ static enum fh_error w_env_query(struct fh_thread_s *fh, const struct fh_word_s
TRY(ds_push(fh, 0)); TRY(ds_push(fh, 0));
} }
#undef EQ
return FH_OK; return FH_OK;
} }

@ -1,7 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_stack.h"
#include "fh_builtins.h"
static enum fh_error w_dupe(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_dupe(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {

@ -1,17 +1,9 @@
#include "forth.h" #include "forth_internal.h"
#include "fh_error.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
// extension // extension
static enum fh_error w_reset(struct fh_thread_s *fh, const struct fh_word_s *w) static enum fh_error w_reset(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
(void) w; (void) w;
enum fh_error rv;
ENSURE_STATE(FH_STATE_INTERPRET); ENSURE_STATE(FH_STATE_INTERPRET);
fh_init(fh); fh_init(fh);
@ -35,10 +27,21 @@ static enum fh_error w_debug(struct fh_thread_s *fh, const struct fh_word_s *w)
return FH_OK; return FH_OK;
} }
static enum fh_error w_exit(struct fh_thread_s *fh, const struct fh_word_s *w)
{
(void) w;
enum fh_error rv;
ENSURE_STATE(FH_STATE_COMPILE);
// let's just hope the return stack is not clobbered!
TRY(fh_put_instr(fh, FH_INSTR_ENDWORD, 0));
return FH_OK;
}
const struct name_and_handler fh_builtins_system[] = { const struct name_and_handler fh_builtins_system[] = {
{"reset", w_reset, 1, 0}, {"reset", w_reset, 1, 0},
{"bye", w_bye, 0, 0}, {"bye", w_bye, 0, 0},
{"debug", w_debug, 0, 0}, {"debug", w_debug, 0, 0},
{"exit", w_exit, 1, 0},
{ /* end marker */ } { /* end marker */ }
}; };

@ -1,10 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_stack.h"
#include "fh_print.h"
#include "fh_builtins.h"
#include "fh_parse.h"
/** /**
* Encode a code point using UTF-8 * Encode a code point using UTF-8
@ -86,7 +80,12 @@ static enum fh_error w_type(struct fh_thread_s *fh, const struct fh_word_s *w)
TRY(ds_pop(fh, &count)); TRY(ds_pop(fh, &count));
TRY(ds_pop(fh, &addr)); TRY(ds_pop(fh, &addr));
FHPRINT("%.*s", count, fh_str_at(fh, addr)); const char *str = fh_str_at(fh, addr);
if (!str) {
LOGE("Type addr out of bounds!");
return FH_ERR_NOT_APPLICABLE;
}
FHPRINT("%.*s", count, str);
return FH_OK; return FH_OK;
} }
@ -161,7 +160,8 @@ static enum fh_error w_s_quote(struct fh_thread_s *fh, const struct fh_word_s *w
TRY(ds_push(fh, len)); TRY(ds_push(fh, len));
} else { } else {
LOG("Compile a string: \"%.*s\"", len, start); LOG("Compile a string: \"%.*s\"", len, start);
instr_init(&instr, FH_INSTR_ALLOCSTR, len); instr.kind = FH_INSTR_ALLOCSTR;
instr.data = len;
fh_heap_write(fh, addr - INSTR_SIZE, &instr, INSTR_SIZE); fh_heap_write(fh, addr - INSTR_SIZE, &instr, INSTR_SIZE);
} }

@ -1,4 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
/** Error names */ /** Error names */
static const char *errornames[FH_ERR_MAX] = { static const char *errornames[FH_ERR_MAX] = {
@ -18,7 +18,7 @@ static const char *errornames[FH_ERR_MAX] = {
[FH_ERR_UNKNOWN_WORD] = "UNKNOWN_WORD", [FH_ERR_UNKNOWN_WORD] = "UNKNOWN_WORD",
[FH_ERR_ILLEGAL_FETCH] = "ILLEGAL_FETCH", [FH_ERR_ILLEGAL_FETCH] = "ILLEGAL_FETCH",
[FH_ERR_ILLEGAL_STORE] = "ILLEGAL_STORE", [FH_ERR_ILLEGAL_STORE] = "ILLEGAL_STORE",
[FH_ERR_DIV_BY_ZERO] = "DIV_BY_ZERO", [FH_ERR_ARITH] = "ARITHMETIC_ERROR",
[FH_ERR_SYNTAX] = "SYNTAX_ERROR", [FH_ERR_SYNTAX] = "SYNTAX_ERROR",
[FH_ERR_NOT_APPLICABLE] = "NOT_APPLICABLE", [FH_ERR_NOT_APPLICABLE] = "NOT_APPLICABLE",
}; };

@ -1,10 +1,4 @@
#include <string.h> #include "forth_internal.h"
#include <assert.h>
#include "fh_print.h"
#include "fh_error.h"
#include "fh_runtime.h"
#include "fh_mem.h"
// Important distinction: HEAP_END is the end of the normally addressable region. HEAP_SIZE is the full memory area. // Important distinction: HEAP_END is the end of the normally addressable region. HEAP_SIZE is the full memory area.
// Buffers are placed at the end of the heap! // Buffers are placed at the end of the heap!
@ -41,6 +35,11 @@ enum fh_error fh_fetch(struct fh_thread_s *fh, uint32_t addr, uint32_t *dst)
LOG("Fetch here %d", *dst); LOG("Fetch here %d", *dst);
break; break;
case MAGICADDR_STATE:
*dst = TOBOOL(fh->state==FH_STATE_COMPILE);
LOG("Fetch state %d", *dst);
break;
case MAGICADDR_INPTR: case MAGICADDR_INPTR:
*dst = fh->inputptr; *dst = fh->inputptr;
LOG("Fetch >IN %d", *dst); LOG("Fetch >IN %d", *dst);
@ -86,6 +85,10 @@ enum fh_error fh_store(struct fh_thread_s *fh, uint32_t addr, uint32_t val)
LOGE("HERE is read-only!"); LOGE("HERE is read-only!");
return FH_ERR_ILLEGAL_STORE; return FH_ERR_ILLEGAL_STORE;
case MAGICADDR_STATE:
LOGE("STATE is read-only!");
return FH_ERR_ILLEGAL_STORE;
case MAGICADDR_INPTR: case MAGICADDR_INPTR:
LOG("set >IN %d", val); LOG("set >IN %d", val);
fh->inputptr = val; fh->inputptr = val;
@ -138,10 +141,8 @@ enum fh_error fh_heap_reserve(
*addr = p; *addr = p;
} }
// Erase the region. This is out of abundance of caution, not really needed if it was erased initially. Maybe. fh->here = p + len;
memset(&fh->heap[p], 0, len); //fh->here = WORDALIGNED(p + len);
fh->here = WORDALIGNED(p + len);
return FH_OK; return FH_OK;
} }
@ -158,6 +159,8 @@ void fh_heap_write(struct fh_thread_s *fh, uint32_t addr, const void *src, uint3
} }
enum fh_error fh_put_instr(struct fh_thread_s *fh, enum fh_instruction_kind kind, uint32_t data) { enum fh_error fh_put_instr(struct fh_thread_s *fh, enum fh_instruction_kind kind, uint32_t data) {
fh_align(fh);
struct fh_instruction_s instr = { struct fh_instruction_s instr = {
.kind = kind, .kind = kind,
.data = data, .data = data,
@ -192,6 +195,7 @@ void fh_heap_copyptr(struct fh_thread_s *fh, uint32_t addr, char * source, uint3
char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) { char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_SIZE) { if (addr >= HEAP_SIZE) {
LOGE("fh_str_at out of bounds!"); LOGE("fh_str_at out of bounds!");
return NULL;
} }
return (char *) &fh->heap[addr]; return (char *) &fh->heap[addr];
} }
@ -199,6 +203,7 @@ char *fh_str_at(struct fh_thread_s *fh, uint32_t addr) {
struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) { struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_END) { if (addr >= HEAP_END) {
LOGE("fh_instr_at out of bounds!"); LOGE("fh_instr_at out of bounds!");
return NULL;
} }
return (void *) &fh->heap[addr]; return (void *) &fh->heap[addr];
} }
@ -206,6 +211,7 @@ struct fh_instruction_s *fh_instr_at(struct fh_thread_s *fh, uint32_t addr) {
struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) { struct fh_word_s *fh_word_at(struct fh_thread_s *fh, uint32_t addr) {
if (addr >= HEAP_END) { if (addr >= HEAP_END) {
LOGE("fh_word_at out of bounds!"); LOGE("fh_word_at out of bounds!");
return NULL;
} }
return (struct fh_word_s *) &fh->heap[addr]; return (struct fh_word_s *) &fh->heap[addr];
} }

@ -1,10 +1,65 @@
#include <ctype.h> #include "forth_internal.h"
#include <stdint.h>
#include <stddef.h> /** True if the character is CR or LF */
#include "fh_print.h" static inline bool isnl(char c)
#include "fh_runtime.h" {
#include "fh_error.h" return c == '\n' || c == '\r';
#include "fh_parse.h" }
/** Process a word read from input */
enum fh_error fh_handle_ascii_word(
struct fh_thread_s *fh,
const char *name,
const size_t wordlen
)
{
enum fh_error rv;
if (wordlen >= MAX_NAME_LEN) {
return FH_ERR_NAME_TOO_LONG;
}
/* First, try if it's a known word */
uint32_t wadr = 0;
if (FH_OK == fh_find_word(fh, name, wordlen, &wadr)) {
TRY(fh_handle_word(fh, wadr));
return FH_OK;
}
/* word not found, try parsing as number */
errno = 0;
char *endptr;
int base = (int) fh->base;
// prefix can override BASE - this is a syntax extension
if (name[0] == '0') {
if (name[1] == 'x') {
base = 16;
} else if (name[1] == 'b') {
base = 2;
} else if (name[1] == 'o') {
base = 8;
}
}
long v = strtol(name, &endptr, base); // XXX if base is 0, this will use auto-detection
if (errno != 0 || (endptr - name) != wordlen) {
LOGE("Unknown word and fail to parse as number: \"%.*s\"", (int) wordlen, name);
return FH_ERR_UNKNOWN_WORD;
}
if (fh->state == FH_STATE_COMPILE) {
LOG("\x1b[34m[COM] Compile number:\x1b[m %ld", v);
TRY(fh_put_instr(fh, FH_INSTR_NUMBER, (uint32_t) v));
} else {
/* interpret */
LOG("\x1b[35m[INT] Push number:\x1b[m %ld", v);
TRY(ds_push(fh, (uint32_t) v));
}
return FH_OK;
}
void fh_input_consume_matching(struct fh_thread_s *fh, chartest_t test, void* param) { void fh_input_consume_matching(struct fh_thread_s *fh, chartest_t test, void* param) {
char *rp = (char *) &fh->heap[INPUTBUF_ADDR + fh->inputptr]; char *rp = (char *) &fh->heap[INPUTBUF_ADDR + fh->inputptr];
@ -141,3 +196,126 @@ enum fh_error fh_input_read_quotedstring(struct fh_thread_s *fh, bool escaped, c
LOGE("String too long!"); LOGE("String too long!");
return FH_ERR_SYNTAX; return FH_ERR_SYNTAX;
} }
/** 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 rv;
#define ReadPtr ((char*)(&fh->heap[INPUTBUF_ADDR + fh->inputptr]))
#define ReadPos (fh->inputptr)
#define ReadLen (fh->inputlen)
fh_fill_input_buffer(fh, linebuf, len);
char c;
if (fh_globals.echo && !fh_globals.interactive) {
LOGI("%s", linebuf);
}
while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) {
c = *ReadPtr;
/* end on newline */
if (isnl(c)) {
goto done;
}
/* skip whitespace */
if (isspace(c)) {
ReadPos++;
continue;
}
const char * const rp = ReadPtr;
char *end;
size_t length;
switch (fh->substate) {
case FH_SUBSTATE_NONE:
/* try to read a word */
end = strchr(rp, ' ');
if (end) {
length = end - rp; /* exclude the space */
} else {
length = strlen(rp);
}
ReadPos += length + 1;
if (EQ(rp, "[if]", length)) {
if (0 == fh->parse_if_level) {
uint32_t val;
TRY(ds_pop(fh, &val));
if (!val) {
LOG("\x1b[32m[if] false, start skipping\x1b[m");
fh->parse_if_level++;
} else {
LOG("\x1b[32m[if] true, proceed\x1b[m");
}
} else {
LOG("\x1b[32m[if] nest+\x1b[m");
fh->parse_if_level++;
}
} else if (EQ(rp, "[else]", length)) {
if (fh->parse_if_level == 1) {
// we got here by running the [if] branch
LOG("\x1b[32m[else] end of false skip\x1b[m");
fh->parse_if_level--;
}
} else if (EQ(rp, "[then]", length)) {
if (fh->parse_if_level > 0) {
fh->parse_if_level--;
if (fh->parse_if_level == 0) {
LOG("\x1b[32m[then] end of skipped section\x1b[m");
} else {
LOG("\x1b[32m[then] nest-\x1b[m");
}
} else {
LOG("\x1b[32m[then] end of conditional\x1b[m");
}
} else if (fh->parse_if_level == 0) {
/* eval a word */
//LOG("Handle \"%.*s\"", (int) length, rp);
TRY(fh_handle_ascii_word(fh, rp, length));
} else {
if (EQ(rp, "\\", length)) {
// discard to EOL
LOG("Discard \"%.*s\"", fh->inputlen - fh->inputptr + length, rp);
goto done;
}
LOG("Discard \"%.*s\"", length, rp);
}
if (!end) {
goto done;
}
break;
case FH_SUBSTATE_PAREN_COMMENT:
end = strchr(rp, ')');
if (end) {
length = end - rp;
LOG("Discard inline comment: \"%.*s\"", length, rp);
fh_setsubstate(fh, FH_SUBSTATE_NONE);
ReadPos += length + 1;
} else {
/* no end, discard all */
LOGE("Unterminated parenthesis comment");
goto done;
}
break;
case FH_SUBSTATE_LINE_COMMENT:
LOG("Discard line comment: \"%.*s\"", fh->inputlen - fh->inputptr, rp);
fh_setsubstate(fh, 0);
goto done; // just discard the rest
default:
LOGE("Bad substate %s", substatenames[fh->substate]);
}
}
done:
//LOG("Line done.");
return FH_OK;
}

@ -1,14 +1,4 @@
#include <string.h> #include "forth_internal.h"
#include <errno.h>
#include <ctype.h>
#include "fh_error.h"
#include "fh_runtime.h"
#include "fh_builtins.h"
#include "fh_stack.h"
#include "fh_mem.h"
#include "fh_globals.h"
#include "fh_print.h"
struct fh_global_s fh_globals = {}; struct fh_global_s fh_globals = {};
@ -29,11 +19,12 @@ static const char *stateshort[FH_STATE_MAX] = {
}; };
/** Sub-state names */ /** Sub-state names */
static const char *substatenames[FH_SUBSTATE_MAX] = { const char *substatenames[FH_SUBSTATE_MAX] = {
[FH_SUBSTATE_NONE] = "NONE", [FH_SUBSTATE_NONE] = "NONE",
[FH_SUBSTATE_PAREN_COMMENT] = "PAREN_COMMENT", [FH_SUBSTATE_PAREN_COMMENT] = "PAREN_COMMENT",
[FH_SUBSTATE_LINE_COMMENT] = "LINE_COMMENT", [FH_SUBSTATE_LINE_COMMENT] = "LINE_COMMENT",
[FH_SUBSTATE_EXIT] = "EXIT", [FH_SUBSTATE_EXIT] = "EXIT",
[FH_SUBSTATE_SKIP_IF] = "SKIP_IF",
}; };
/** Sub-state names */ /** Sub-state names */
@ -76,7 +67,9 @@ enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh)
//LOG("Added word \"%s\" at 0x%08x", w->name, ptr); //LOG("Added word \"%s\" at 0x%08x", w->name, ptr);
// thread it onto the linked list // thread it onto the linked list
fh_word_at(fh, ptr)->previous = fh->dict_last; struct fh_word_s *word = fh_word_at(fh, ptr);
if (!word) return FH_ERR_INTERNAL;
word->previous = fh->dict_last;
fh->dict_last = ptr; fh->dict_last = ptr;
return FH_OK; return FH_OK;
@ -132,6 +125,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
// make sure it's aligned // make sure it's aligned
fh->execptr = WORDALIGNED(fh->execptr); fh->execptr = WORDALIGNED(fh->execptr);
const struct fh_instruction_s *instr = fh_instr_at(fh, fh->execptr); const struct fh_instruction_s *instr = fh_instr_at(fh, fh->execptr);
if (!instr) {
LOGE("Execution pointer out of bounds!");
return FH_ERR_INTERNAL;
}
fh->execptr += INSTR_SIZE; fh->execptr += INSTR_SIZE;
uint32_t strl; uint32_t strl;
@ -145,8 +142,12 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
goto instr; goto instr;
case FH_INSTR_POSTPONED_WORD: case FH_INSTR_POSTPONED_WORD:
if (fh->state == FH_STATE_COMPILE) { //if (fh->state == FH_STATE_COMPILE) {
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
if (!w2) {
LOGE("Postponed bad word addr!");
return FH_ERR_INTERNAL;
}
if (w2->flags & WORDFLAG_IMMEDIATE) { if (w2->flags & WORDFLAG_IMMEDIATE) {
LOG("Call immediate postponed word: %s", w2->name); LOG("Call immediate postponed word: %s", w2->name);
TRY(w2->handler(fh, w2)); TRY(w2->handler(fh, w2));
@ -154,17 +155,21 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
LOG("Add postponed word: %s", w2->name); LOG("Add postponed word: %s", w2->name);
TRY(fh_put_instr(fh, FH_INSTR_WORD, instr->data)); TRY(fh_put_instr(fh, FH_INSTR_WORD, instr->data));
} }
} else { /*} else {
LOGE("Postpone in interpret mode!"); LOGE("Postpone in interpret mode!");
goto end; goto end;
} }*/
goto instr; goto instr;
case FH_INSTR_WORD: case FH_INSTR_WORD:
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
if (!w2) {
LOGE("Instr bad word addr!");
return FH_ERR_INTERNAL;
}
if (w2->flags & WORDFLAG_BUILTIN) { if (w2->flags & WORDFLAG_BUILTIN) {
LOG("Exec: native-word \"%s\"", w2->name); LOG("Exec: native-word \"%s\"", w2->name);
w2->handler(fh, w2); TRY(w2->handler(fh, w2));
if (fh->substate == FH_SUBSTATE_EXIT) { if (fh->substate == FH_SUBSTATE_EXIT) {
fh_setsubstate(fh, 0); fh_setsubstate(fh, 0);
LOG("Exec: early return"); LOG("Exec: early return");
@ -177,6 +182,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
} else { } else {
LOG("Exec: user-word %s (CALL)", w2->name); LOG("Exec: user-word %s (CALL)", w2->name);
w = fh_word_at(fh, instr->data); w = fh_word_at(fh, instr->data);
if (!w) {
LOGE("CALL instr bad word addr!");
return FH_ERR_INTERNAL;
}
goto call; goto call;
} }
@ -203,6 +212,10 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
case FH_INSTR_TO: case FH_INSTR_TO:
TRY(ds_pop(fh, &val)); TRY(ds_pop(fh, &val));
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
if (!w2) {
LOGE("TO instr bad variable addr!");
return FH_ERR_INTERNAL;
}
LOG("Exec: %d->%s", val, w2->name); LOG("Exec: %d->%s", val, w2->name);
if (w2->flags & WORDFLAG_CONSTANT) { if (w2->flags & WORDFLAG_CONSTANT) {
@ -255,7 +268,8 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
LOG("after add: %d", fh->loop_i); LOG("after add: %d", fh->loop_i);
if (((int32_t)index0 < (int32_t)limit) == ((int32_t)fh->loop_i < (int32_t)limit)) { // boundary not crossed, continue // FIXME this is probably wrong
if (((int32_t)index0 < (int32_t)limit) == ((int32_t)fh->loop_i < (int32_t)limit) && fh->loop_i != limit) { // boundary not crossed, continue
fh->execptr = instr->data; // go to beginning fh->execptr = instr->data; // go to beginning
} else { } else {
// end of loop // end of loop
@ -290,7 +304,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh, const struct fh_word_s *w0)
goto instr; goto instr;
case FH_INSTR_ENDWORD: case FH_INSTR_ENDWORD:
LOG("Exec: word-end (RETURN)"); LOG("Exec: word-end");
TRY(rs_pop(fh, &fh->execptr)); TRY(rs_pop(fh, &fh->execptr));
if (fh->execptr == MAGICADDR_EXEC_INTERACTIVE) { if (fh->execptr == MAGICADDR_EXEC_INTERACTIVE) {
goto end; goto end;
@ -323,6 +337,7 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr)
{ {
enum fh_error rv; enum fh_error rv;
struct fh_word_s *w = fh_word_at(fh, addr); struct fh_word_s *w = fh_word_at(fh, addr);
if (!w) return FH_ERR_INTERNAL;
if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) { if (fh->state == FH_STATE_COMPILE && 0 == (w->flags & WORDFLAG_IMMEDIATE)) {
LOG("\x1b[34m[%s] Compile word:\x1b[m %s", stateshort[fh->state], w->name); LOG("\x1b[34m[%s] Compile word:\x1b[m %s", stateshort[fh->state], w->name);
TRY(fh_put_instr(fh, FH_INSTR_WORD, addr)); TRY(fh_put_instr(fh, FH_INSTR_WORD, addr));
@ -341,12 +356,18 @@ enum fh_error fh_handle_word(struct fh_thread_s *fh, uint32_t addr)
enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out) enum fh_error fh_find_word(struct fh_thread_s *fh, const char *name, size_t wordlen, uint32_t *addr_out)
{ {
if (name == NULL) {
return FH_ERR_UNKNOWN_WORD;
}
if (wordlen == 0) { if (wordlen == 0) {
wordlen = strlen(name); wordlen = strlen(name);
} }
uint32_t addr = fh->dict_last; uint32_t addr = fh->dict_last;
while (addr != MAGICADDR_DICTFIRST) { while (addr != MAGICADDR_DICTFIRST) {
struct fh_word_s *w = fh_word_at(fh, addr); struct fh_word_s *w = fh_word_at(fh, addr);
if (!w) {
break;
}
if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) { if (0 == strncasecmp(name, w->name, wordlen) && w->name[wordlen] == 0) {
if (addr_out) { if (addr_out) {
*addr_out = addr; *addr_out = addr;
@ -377,61 +398,6 @@ enum fh_error fh_loop_unnest(struct fh_thread_s *fh)
return FH_OK; return FH_OK;
} }
/** Process a word read from input */
static enum fh_error fh_handle_ascii_word(
struct fh_thread_s *fh,
const char *name,
const size_t wordlen
)
{
enum fh_error rv;
if (wordlen >= MAX_NAME_LEN) {
return FH_ERR_NAME_TOO_LONG;
}
/* First, try if it's a known word */
uint32_t wadr = 0;
if (FH_OK == fh_find_word(fh, name, wordlen, &wadr)) {
TRY(fh_handle_word(fh, wadr));
return FH_OK;
}
/* word not found, try parsing as number */
errno = 0;
char *endptr;
int base = (int) fh->base;
// prefix can override BASE - this is a syntax extension
if (name[0] == '0') {
if (name[1] == 'x') {
base = 16;
} else if (name[1] == 'b') {
base = 2;
} else if (name[1] == 'o') {
base = 8;
}
}
long v = strtol(name, &endptr, base); // XXX if base is 0, this will use auto-detection
if (errno != 0 || (endptr - name) != wordlen) {
LOGE("Unknown word and fail to parse as number: \"%.*s\"", (int) wordlen, name);
return FH_ERR_UNKNOWN_WORD;
}
struct fh_instruction_s instr;
if (fh->state == FH_STATE_COMPILE) {
LOG("Compile number: %ld", v);
TRY(fh_put_instr(fh, FH_INSTR_NUMBER, (uint32_t) v));
} else {
/* interpret */
LOG("Interpret number: %ld", v);
TRY(ds_push(fh, (uint32_t) v));
}
return FH_OK;
}
/** Postpone a word */ /** Postpone a word */
enum fh_error fh_postpone_word( enum fh_error fh_postpone_word(
@ -449,91 +415,3 @@ enum fh_error fh_postpone_word(
return FH_OK; return FH_OK;
} }
/** True if the character is CR or LF */
static inline bool isnl(char c)
{
return c == '\n' || c == '\r';
}
/** 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 rv;
#define ReadPtr ((char*)(&fh->heap[INPUTBUF_ADDR + fh->inputptr]))
#define ReadPos (fh->inputptr)
#define ReadLen (fh->inputlen)
fh_fill_input_buffer(fh, linebuf, len);
char c;
if (fh_globals.echo && !fh_globals.interactive) {
LOGI("%s", linebuf);
}
while (ReadPos < ReadLen && fh->state != FH_STATE_SHUTDOWN) {
c = *ReadPtr;
/* end on newline */
if (isnl(c)) {
goto done;
}
/* skip whitespace */
if (isspace(c)) {
ReadPos++;
continue;
}
const char * const rp = ReadPtr;
char *end;
size_t length;
switch (fh->substate) {
case FH_SUBSTATE_NONE:
/* try to read a word */
end = strchr(rp, ' ');
if (end) {
length = end - rp; /* exclude the space */
} else {
length = strlen(rp);
}
ReadPos += length + 1;
/* eval a word */
//LOG("Handle \"%.*s\"", (int) length, rp);
TRY(fh_handle_ascii_word(fh, rp, length));
if (!end) {
goto done;
}
break;
case FH_SUBSTATE_PAREN_COMMENT:
end = strchr(rp, ')');
if (end) {
length = end - rp;
LOG("Discard inline comment");
fh_setsubstate(fh, FH_SUBSTATE_NONE);
ReadPos += length + 1;
} else {
/* no end, discard all */
LOGE("Unterminated parenthesis comment");
goto done;
}
break;
case FH_SUBSTATE_LINE_COMMENT:
LOG("Discard line comment");
fh_setsubstate(fh, 0);
goto done; // just discard the rest
default:
LOGE("Bad substate %s", substatenames[fh->substate]);
}
}
done:
LOG("Line done.");
return FH_OK;
}

@ -1,20 +1,26 @@
#include "forth.h" #include "forth_internal.h"
#include "fh_runtime.h"
#include "fh_mem.h"
#include "fh_print.h"
static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w) static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
{ {
if (!w) {
LOGE("NULL!");
return;
}
if (w->flags & WORDFLAG_WORD) { if (w->flags & WORDFLAG_WORD) {
if (w->handler == w_user_word) { if (w->handler == w_user_word) {
uint32_t execptr = w->param; uint32_t execptr = w->param;
FHPRINT("Compiled word %s%s\n", w->name, (w->flags&WORDFLAG_IMMEDIATE) ? " IMMEDIATE" : ""); FHPRINT("Compiled word %s%s\n", w->name, (w->flags & WORDFLAG_IMMEDIATE) ? " IMMEDIATE" : "");
while (1) { while (1) {
// make sure it's aligned // make sure it's aligned
execptr = WORDALIGNED(execptr); execptr = WORDALIGNED(execptr);
FHPRINT("0x%08x: ", execptr); FHPRINT("0x%08x: ", execptr);
const struct fh_instruction_s *instr = fh_instr_at(fh, execptr); const struct fh_instruction_s *instr = fh_instr_at(fh, execptr);
if (!instr) {
LOGE("Word pointer out of bounds!");
return;
}
execptr += INSTR_SIZE; execptr += INSTR_SIZE;
uint32_t strl; uint32_t strl;
@ -26,24 +32,36 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
case FH_INSTR_WORD: case FH_INSTR_WORD:
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
FHPRINT("Call(word %s)\n", w2->name); if (w2) {
FHPRINT("Call(word %s)\n", w2->name);
} else {
FHPRINT("Call(BAD ADDRESS!!! 0x%08x)\n", instr->data);
}
break; break;
case FH_INSTR_POSTPONED_WORD: case FH_INSTR_POSTPONED_WORD:
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) { if (w2) {
FHPRINT("Postpone(word %s)\n", w2->name); if (w2->name[0]) {
FHPRINT("Postpone(word %s)\n", w2->name);
} else {
FHPRINT("Postpone(word 0x%08x)\n", instr->data);
}
} else { } else {
FHPRINT("Postpone(word 0x%08x)\n", instr->data); FHPRINT("Postpone(BAD ADDRESS!!! 0x%08x)\n", instr->data);
} }
break; break;
case FH_INSTR_TO: case FH_INSTR_TO:
w2 = fh_word_at(fh, instr->data); w2 = fh_word_at(fh, instr->data);
if (w2->name[0]) { if (w2) {
FHPRINT("To(var %s)\n", w2->name); if (w2->name[0]) {
FHPRINT("To(var %s)\n", w2->name);
} else {
FHPRINT("To(var 0x%08x)\n", instr->data);
}
} else { } else {
FHPRINT("To(var 0x%08x)\n", instr->data); FHPRINT("To(BAD ADDRESS!!! 0x%08x)\n", instr->data);
} }
break; break;
@ -96,13 +114,13 @@ static void show_word(struct fh_thread_s *fh, const struct fh_word_s *w)
FHPRINT("Built-in word %s\n", w->name); FHPRINT("Built-in word %s\n", w->name);
}; };
} else if (w->flags & WORDFLAG_VARIABLE) { } else if (w->flags & WORDFLAG_VARIABLE) {
FHPRINT("Variable %s, value %d (0x%08x)\n", w->name, (int32_t)w->param, w->param); FHPRINT("Variable %s, value %d (0x%08x)\n", w->name, (int32_t) w->param, w->param);
} else if (w->flags & WORDFLAG_CONSTANT) { } else if (w->flags & WORDFLAG_CONSTANT) {
FHPRINT("Constant %s, value %d (0x%08x)\n", w->name, (int32_t)w->param, w->param); FHPRINT("Constant %s, value %d (0x%08x)\n", w->name, (int32_t) w->param, w->param);
} else if (w->flags & WORDFLAG_CREATED) { } else if (w->flags & WORDFLAG_CREATED) {
FHPRINT("CREATE'd entry %s, param %d (0x%08x)\n", w->name, (int32_t)w->param, w->param); FHPRINT("CREATE'd entry %s, param %d (0x%08x)\n", w->name, (int32_t) w->param, w->param);
} else { } else {
FHPRINT("Unknown entry %s, param %d (0x%08x)\n", w->name, (int32_t)w->param, w->param); FHPRINT("Unknown entry %s, param %d (0x%08x)\n", w->name, (int32_t) w->param, w->param);
} }
} }

@ -1,8 +1,4 @@
#include "fh_error.h" #include "forth_internal.h"
#include "fh_config.h"
#include "fh_runtime.h"
#include "fh_stack.h"
#include "fh_print.h"
// TODO stacks should grow down, not up! // TODO stacks should grow down, not up!
@ -86,6 +82,24 @@ enum fh_error rs_pop(struct fh_thread_s *fh, uint32_t *out)
} \ } \
} while(0) } while(0)
enum fh_error ds_push_dw(struct fh_thread_s *fh, uint64_t in)
{
enum fh_error rv;
TRY(ds_push(fh, in & 0xFFFFFFFFULL));
TRY(ds_push(fh, (in & 0xFFFFFFFF00000000ULL) >> 32));
return FH_OK;
}
enum fh_error ds_pop_dw(struct fh_thread_s *fh, uint64_t *out)
{
enum fh_error rv;
uint32_t a, b;
TRY(ds_pop(fh, &a));
TRY(ds_pop(fh, &b));
*out = ((uint64_t)a << 32) | ((uint64_t)b);
return FH_OK;
}
/** Push to data stack */ /** Push to data stack */
enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in) enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in)
{ {

@ -5,8 +5,6 @@
#include <ctype.h> #include <ctype.h>
#include "forth.h" #include "forth.h"
#include "fh_runtime.h"
#include "fh_print.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -70,7 +68,9 @@ int main(int argc, char *argv[])
/* process input line by line */ /* process input line by line */
int linecnt = 0; int linecnt = 0;
char linebuf[MAXLINE]; char linebuf[MAXLINE];
FHPRINT("%s", prompt); if (fh_globals.interactive) {
FHPRINT("%s", prompt);
}
while (fh.state != FH_STATE_SHUTDOWN && fgets(linebuf, MAXLINE, infile)) { while (fh.state != FH_STATE_SHUTDOWN && fgets(linebuf, MAXLINE, infile)) {
linecnt++; linecnt++;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,33 @@
1 [if] ." One,"
0
[if] bla bla
[if]
[if] this
[then]
is
[then]
not parsed
[else]
." Two,"
[then]
." Bla bla,"
[then]
." End,"
0
[if]
blabla
[else]
1
[if]
char A EMIT
[else]
[if]
[if] this
[then]
is not parsed
[then]
[then]
[then]
char X EMIT
Loading…
Cancel
Save