add comments

master
Ondřej Hruška 3 years ago
parent c225928b54
commit 15848e203b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 0
      a.forth
  2. 2
      hello.forth
  3. 158
      main.c
  4. 0
      str.forth

@ -0,0 +1,2 @@
: hi ." Hello, World!" ;
hi

158
main.c

@ -4,11 +4,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <unistd.h>
#define CONTROL_STACK_DEPTH 1024 #define CONTROL_STACK_DEPTH 1024
#define DATA_STACK_DEPTH 1024 #define DATA_STACK_DEPTH 1024
#define RETURN_STACK_DEPTH 1024 #define RETURN_STACK_DEPTH 1024
#define MAX_NAME_LEN 64 #define MAX_NAME_LEN 32
#define DICT_SIZE 1024 #define DICT_SIZE 1024
#define COMPILED_BUFFER_SIZE (1024*1024) #define COMPILED_BUFFER_SIZE (1024*1024)
#define HEAP_SIZE (1024*1024) #define HEAP_SIZE (1024*1024)
@ -18,27 +19,38 @@ struct fh_thread_s;
struct fh_word_s; struct fh_word_s;
struct fh_instruction_s; struct fh_instruction_s;
/** Forth runtime global state */
struct fh_global_s { struct fh_global_s {
/** Verbose logging enabled */
bool verbose; bool verbose;
/** Interactive mode (i.e. not started with a file argument) */
bool interactive; bool interactive;
} fh_globals = {}; } fh_globals = {};
/* if the return address is this, we should drop back to interactive mode */ /* if the return address is this, we should drop back to interactive mode */
#define MAGICADDR_INTERACTIVE 0xFFFFFFFFULL #define MAGICADDR_INTERACTIVE 0xFFFFFFFFULL
#define ALIGNWORD(var) \ /** Get a value rounded up to multiple of word size */
do { \ #define WORDALIGNED(var) (((var) + 3) & ~3)
while (((var) % 4) != 0) { (var)++; } \
} while (0) _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");
/* logging, TODO make levels configurable */ /* logging */
#define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\r\n", ##__VA_ARGS__); } } while (0) #define LOG(format, ...) do { if(fh_globals.verbose) { fprintf(stderr, format "\r\n", ##__VA_ARGS__); } } while (0)
#define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\r\n", ##__VA_ARGS__) #define LOGI(format, ...) fprintf(stderr, "\x1b[32m" format "\x1b[m\r\n", ##__VA_ARGS__)
#define LOGE(format, ...) fprintf(stderr, "\x1b[31;1m" format "\x1b[m\r\n", ##__VA_ARGS__) #define LOGE(format, ...) fprintf(stderr, "\x1b[31;1m" format "\x1b[m\r\n", ##__VA_ARGS__)
/* Forth standard output. XXX should be stdout, but then colors get mangled */ /* Forth standard output. XXX should be stdout, but then colors get mangled if logging is used */
#define FHPRINT(format, ...) fprintf(stderr, "\x1b[33;1m" format "\x1b[m", ##__VA_ARGS__) #define FHPRINT(format, ...) fprintf(stderr, "\x1b[33;1m" format "\x1b[m", ##__VA_ARGS__)
#define FHPRINT_SVC(format, ...) fprintf(stderr, "" format "", ##__VA_ARGS__) #define FHPRINT_SVC(format, ...) fprintf(stderr, "" format "", ##__VA_ARGS__)
/** Error codes */
enum fh_error { enum fh_error {
FH_OK = 0, FH_OK = 0,
FH_ERR_CS_OVERFLOW, FH_ERR_CS_OVERFLOW,
@ -57,6 +69,7 @@ enum fh_error {
FH_ERR_MAX, FH_ERR_MAX,
}; };
/** Error names */
static const char *errornames[] = { static const char *errornames[] = {
[FH_OK] = "OK", [FH_OK] = "OK",
[FH_ERR_CS_OVERFLOW] = "CS_OVERFLOW", [FH_ERR_CS_OVERFLOW] = "CS_OVERFLOW",
@ -74,6 +87,7 @@ static const char *errornames[] = {
[FH_ERR_UNKNOWN_WORD] = "UNKNOWN_WORD", [FH_ERR_UNKNOWN_WORD] = "UNKNOWN_WORD",
}; };
/** Get error name from code, returns Unknown if not defined */
const char *fherr_name(enum fh_error e) const char *fherr_name(enum fh_error e)
{ {
if (e >= FH_ERR_MAX) { if (e >= FH_ERR_MAX) {
@ -82,39 +96,59 @@ const char *fherr_name(enum fh_error e)
return errornames[e]; return errornames[e];
} }
/** Word handler typedef */
typedef enum fh_error (*word_exec_t)(struct fh_thread_s *fh); typedef enum fh_error (*word_exec_t)(struct fh_thread_s *fh);
/** Word struct as they are stored in the dictionary */
struct fh_word_s { struct fh_word_s {
/** Word name */
char name[MAX_NAME_LEN]; char name[MAX_NAME_LEN];
/**
* Handler function.
* Builtin functions use pre-defined native handlers.
* User words use a shared handler that executes compiled
* bytecode at 'start' address of the compile-memory area.
*/
word_exec_t handler; word_exec_t handler;
/** Indicates that this is a built-in instruction and not a word call */
bool builtin; bool builtin;
/** Indicates that this instruction should always be treated as interpreted,
* in practice this is only used for `;` */
bool immediate; bool immediate;
/** Start address in case of user words */
uint32_t start; uint32_t start;
uint32_t end;
}; };
/** Bytecode instruction type marker */
enum fb_instruction_kind { enum fb_instruction_kind {
/* Data is a word number in the dict */ /* Data = word pointer (dict index) */
FH_INSTR_WORD, FH_INSTR_WORD,
/* Data is a numeric value to push on the data stack */ /* Data = numeric value to push onto the data stack */
FH_INSTR_NUMBER, FH_INSTR_NUMBER,
}; };
/** One instruction in bytecode */
struct fh_instruction_s { struct fh_instruction_s {
/** What is the meaning of data? */
enum fb_instruction_kind kind; enum fb_instruction_kind kind;
/** Data word */
uint32_t data; uint32_t data;
}; };
/** words that are not in the dict, have special effect */ /** Bytecode word indices that are not in the dict, have special effect */
enum compiler_word { enum compiler_word {
/** End of a user defined word, pop address and jump back */
CPLWORD_ENDWORD = DICT_SIZE + 1, CPLWORD_ENDWORD = DICT_SIZE + 1,
/** This is the `s"` instruction, the length (u32) and string data immediately follow */
CPLWORD_ALLOCSTR, CPLWORD_ALLOCSTR,
/** This is the `."` instruction, same format as above. */
CPLWORD_TYPESTR, CPLWORD_TYPESTR,
}; };
_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");
/** Forth runtime major state */
enum fh_state { enum fh_state {
FH_STATE_INTERPRET = 0, FH_STATE_INTERPRET = 0,
FH_STATE_COMPILE, FH_STATE_COMPILE,
@ -122,12 +156,14 @@ enum fh_state {
FH_STATE_MAX, FH_STATE_MAX,
}; };
/** State names */
static const char *statenames[] = { static const char *statenames[] = {
[FH_STATE_INTERPRET] = "INTERPRET", [FH_STATE_INTERPRET] = "INTERPRET",
[FH_STATE_COMPILE] = "COMPILE", [FH_STATE_COMPILE] = "COMPILE",
[FH_STATE_SHUTDOWN] = "SHUTDOWN", [FH_STATE_SHUTDOWN] = "SHUTDOWN",
}; };
/** Forth runtime minor state */
enum fh_substate { enum fh_substate {
FH_SUBSTATE_NONE = 0, FH_SUBSTATE_NONE = 0,
FH_SUBSTATE_COLONNAME, FH_SUBSTATE_COLONNAME,
@ -138,6 +174,7 @@ enum fh_substate {
FH_SUBSTATE_MAX, FH_SUBSTATE_MAX,
}; };
/** Sub-state names */
static const char *substatenames[] = { static const char *substatenames[] = {
[FH_SUBSTATE_NONE] = "NONE", [FH_SUBSTATE_NONE] = "NONE",
[FH_SUBSTATE_COLONNAME] = "COLONNAME", [FH_SUBSTATE_COLONNAME] = "COLONNAME",
@ -147,6 +184,12 @@ static const char *substatenames[] = {
[FH_SUBSTATE_LINECOMMENT] = "LINECOMMENT", [FH_SUBSTATE_LINECOMMENT] = "LINECOMMENT",
}; };
/**
* Forth runtime instance - state variables and memory areas.
*
* Some memory areas, such as the dict or heap, could be moved
* to a shared pointer if multi-threading and synchronization is added.
*/
struct fh_thread_s { struct fh_thread_s {
/** Control stack */ /** Control stack */
uint32_t control_stack[CONTROL_STACK_DEPTH]; uint32_t control_stack[CONTROL_STACK_DEPTH];
@ -176,8 +219,10 @@ struct fh_thread_s {
/** Forth state */ /** Forth state */
enum fh_state state; enum fh_state state;
/** Forth sub-state */ /** Forth sub-state */
enum fh_substate substate; enum fh_substate substate;
/** Word currently being executed - a pointer is placed here /** Word currently being executed - a pointer is placed here
* before calling the handler */ * before calling the handler */
struct fh_word_s *exec_word; struct fh_word_s *exec_word;
@ -188,11 +233,6 @@ struct fh_thread_s {
if (FH_OK != (rv = (x))) return rv; \ if (FH_OK != (rv = (x))) return rv; \
} while (0) } while (0)
#define TRY_FAIL(x) \
do { \
if (FH_OK != (rv = (x))) goto fail; \
} while (0)
/** Add a word to the dictionary. */ /** Add a word to the dictionary. */
enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh) enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh)
{ {
@ -203,8 +243,7 @@ enum fh_error fh_add_word(const struct fh_word_s *w, struct fh_thread_s *fh)
return FH_OK; return FH_OK;
} }
//region Push & Pop /** Pop from data stack */
static inline enum fh_error ds_pop(struct fh_thread_s *fh, uint32_t *out) static inline enum fh_error ds_pop(struct fh_thread_s *fh, uint32_t *out)
{ {
if (fh->data_stack_top == 0) { if (fh->data_stack_top == 0) {
@ -216,6 +255,7 @@ static inline enum fh_error ds_pop(struct fh_thread_s *fh, uint32_t *out)
return FH_OK; return FH_OK;
} }
/** Pop from return stack */
static inline enum fh_error rs_pop(struct fh_thread_s *fh, uint32_t *out) static inline enum fh_error rs_pop(struct fh_thread_s *fh, uint32_t *out)
{ {
if (fh->return_stack_top == 0) { if (fh->return_stack_top == 0) {
@ -227,6 +267,7 @@ static inline enum fh_error rs_pop(struct fh_thread_s *fh, uint32_t *out)
return FH_OK; return FH_OK;
} }
/** Pop from control stack */
static inline enum fh_error cs_pop(struct fh_thread_s *fh, uint32_t *out) static inline enum fh_error cs_pop(struct fh_thread_s *fh, uint32_t *out)
{ {
if (fh->control_stack_top == 0) { if (fh->control_stack_top == 0) {
@ -238,6 +279,7 @@ static inline enum fh_error cs_pop(struct fh_thread_s *fh, uint32_t *out)
return FH_OK; return FH_OK;
} }
/** Push to data stack */
static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in) static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in)
{ {
LOG("DS push %d", in); LOG("DS push %d", in);
@ -248,6 +290,7 @@ static inline enum fh_error ds_push(struct fh_thread_s *fh, uint32_t in)
return FH_OK; return FH_OK;
} }
/** Push to return stack */
static inline enum fh_error rs_push(struct fh_thread_s *fh, uint32_t in) static inline enum fh_error rs_push(struct fh_thread_s *fh, uint32_t in)
{ {
LOG("RS push %d", in); LOG("RS push %d", in);
@ -258,6 +301,7 @@ static inline enum fh_error rs_push(struct fh_thread_s *fh, uint32_t in)
return FH_OK; return FH_OK;
} }
/** Push to control stack */
static inline enum fh_error cs_push(struct fh_thread_s *fh, uint32_t in) static inline enum fh_error cs_push(struct fh_thread_s *fh, uint32_t in)
{ {
LOG("CS push %d", in); LOG("CS push %d", in);
@ -268,16 +312,17 @@ static inline enum fh_error cs_push(struct fh_thread_s *fh, uint32_t in)
return FH_OK; return FH_OK;
} }
//endregion Push & Pop /** Log current runtime state */
static void showstate(const struct fh_thread_s *fh)
static void showstate(const struct fh_thread_s *fh) { {
if(fh->substate==0) { if (fh->substate == 0) {
LOG("state = %s", statenames[fh->state]); LOG("state = %s", statenames[fh->state]);
} else { } else {
LOG("state = %s.%s", statenames[fh->state], substatenames[fh->substate]); LOG("state = %s.%s", statenames[fh->state], substatenames[fh->substate]);
} }
} }
/** Set runtime state and sub-state */
void fh_setstate(struct fh_thread_s *fh, enum fh_state state, enum fh_substate substate) void fh_setstate(struct fh_thread_s *fh, enum fh_state state, enum fh_substate substate)
{ {
fh->state = state; fh->state = state;
@ -285,20 +330,21 @@ void fh_setstate(struct fh_thread_s *fh, enum fh_state state, enum fh_substate s
showstate(fh); showstate(fh);
} }
/** Set runtime sub-state (state is unchanged) */
void fh_setsubstate(struct fh_thread_s *fh, enum fh_substate substate) void fh_setsubstate(struct fh_thread_s *fh, enum fh_substate substate)
{ {
fh->substate = substate; fh->substate = substate;
showstate(fh); showstate(fh);
} }
enum fh_error fh_allot( /** Allocate a heap region, e.g. for a string. The address is stored to `addr` */
enum fh_error fh_heap_reserve(
struct fh_thread_s *fh, struct fh_thread_s *fh,
size_t len, size_t len,
uint32_t *addr uint32_t *addr
) )
{ {
uint32_t p = fh->heap_top; uint32_t p = WORDALIGNED(fh->heap_top); // FIXME this shouldn't be needed
ALIGNWORD(p);
if (p + len > HEAP_SIZE) { if (p + len > HEAP_SIZE) {
return FH_ERR_HEAP_FULL; return FH_ERR_HEAP_FULL;
@ -306,22 +352,19 @@ enum fh_error fh_allot(
*addr = p; *addr = p;
size_t next = p + len; fh->heap_top = WORDALIGNED(p + len);
ALIGNWORD(next);
fh->heap_top = next;
return FH_OK; return FH_OK;
} }
/** Reserve space in the compile memory area */
enum fh_error fh_compile_reserve( enum fh_error fh_compile_reserve(
struct fh_thread_s *fh, struct fh_thread_s *fh,
size_t len, size_t len,
uint32_t *addr uint32_t *addr
) )
{ {
uint32_t p = fh->compile_top; uint32_t p = WORDALIGNED(fh->compile_top); // FIXME this shouldn't be needed
// align up
ALIGNWORD(p);
if (p + len > COMPILED_BUFFER_SIZE) { if (p + len > COMPILED_BUFFER_SIZE) {
return FH_ERR_HEAP_FULL; return FH_ERR_HEAP_FULL;
@ -329,16 +372,11 @@ enum fh_error fh_compile_reserve(
*addr = p; *addr = p;
size_t next = p + len; fh->compile_top = WORDALIGNED(p + len);
ALIGNWORD(next);
fh->compile_top = next;
return FH_OK; return FH_OK;
} }
//region Builtin Words
enum fh_error w_add(struct fh_thread_s *fh) enum fh_error w_add(struct fh_thread_s *fh)
{ {
enum fh_error rv; enum fh_error rv;
@ -387,7 +425,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh)
instr:; instr:;
// make sure it's aligned // make sure it's aligned
ALIGNWORD(fh->execptr); fh->execptr = WORDALIGNED(fh->execptr);
const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[fh->execptr]; const struct fh_instruction_s *instr = (const struct fh_instruction_s *) &fh->compile[fh->execptr];
fh->execptr += sizeof(struct fh_instruction_s); fh->execptr += sizeof(struct fh_instruction_s);
@ -406,7 +444,7 @@ enum fh_error w_user_word(struct fh_thread_s *fh)
strl = *((uint32_t *) &fh->compile[fh->execptr]); strl = *((uint32_t *) &fh->compile[fh->execptr]);
fh->execptr += 4; fh->execptr += 4;
if (wn == CPLWORD_ALLOCSTR) { if (wn == CPLWORD_ALLOCSTR) {
TRY(fh_allot(fh, strl, &addr)); TRY(fh_heap_reserve(fh, strl, &addr));
memcpy(&fh->heap[addr], &fh->compile[fh->execptr], strl); memcpy(&fh->heap[addr], &fh->compile[fh->execptr], strl);
LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]); LOG("Exec: alloc-str \"%.*s\"", strl, &fh->heap[addr]);
TRY(ds_push(fh, addr)); TRY(ds_push(fh, addr));
@ -477,7 +515,6 @@ enum fh_error w_semicolon(struct fh_thread_s *fh)
/* Return to interpret state */ /* Return to interpret state */
fh_setstate(fh, FH_STATE_INTERPRET, 0); fh_setstate(fh, FH_STATE_INTERPRET, 0);
fh->dict[fh->dict_top].end = fh->compile_top; /* one past the end cell */
fh->dict_top++; fh->dict_top++;
return FH_OK; return FH_OK;
} }
@ -505,12 +542,14 @@ enum fh_error w_type(struct fh_thread_s *fh)
enum fh_error w_cr(struct fh_thread_s *fh) enum fh_error w_cr(struct fh_thread_s *fh)
{ {
(void) fh;
FHPRINT("\r\n"); FHPRINT("\r\n");
return FH_OK; return FH_OK;
} }
enum fh_error w_space(struct fh_thread_s *fh) enum fh_error w_space(struct fh_thread_s *fh)
{ {
(void) fh;
FHPRINT(" "); FHPRINT(" ");
return FH_OK; return FH_OK;
} }
@ -546,6 +585,7 @@ enum fh_error w_bye(struct fh_thread_s *fh)
return FH_OK; 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) enum fh_error register_builtin_words(struct fh_thread_s *fh)
{ {
struct name_and_handler { struct name_and_handler {
@ -575,6 +615,7 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
{ /* end marker */ } { /* end marker */ }
}; };
// foreach
struct fh_word_s w; struct fh_word_s w;
const struct name_and_handler *p = builtins; const struct name_and_handler *p = builtins;
enum fh_error rv; enum fh_error rv;
@ -594,8 +635,7 @@ enum fh_error register_builtin_words(struct fh_thread_s *fh)
#undef ADDWORD #undef ADDWORD
//endregion Builtin Words /** Initialize a runtime */
enum fh_error fh_init_thread(struct fh_thread_s *fh) enum fh_error fh_init_thread(struct fh_thread_s *fh)
{ {
enum fh_error rv; enum fh_error rv;
@ -609,7 +649,8 @@ enum fh_error fh_init_thread(struct fh_thread_s *fh)
return FH_OK; return FH_OK;
} }
enum fh_error fh_handle_quoted_string( /** Process a quoted string read from input */
static enum fh_error fh_handle_quoted_string(
struct fh_thread_s *fh, struct fh_thread_s *fh,
char *start, char *start,
size_t len size_t len
@ -623,7 +664,7 @@ enum fh_error fh_handle_quoted_string(
if (fh->state == FH_STATE_INTERPRET) { if (fh->state == FH_STATE_INTERPRET) {
switch (fh->substate) { switch (fh->substate) {
case FH_SUBSTATE_SQUOTE: case FH_SUBSTATE_SQUOTE:
TRY(fh_allot(fh, len, &addr)); TRY(fh_heap_reserve(fh, len, &addr));
memcpy(&fh->heap[addr], start, len); memcpy(&fh->heap[addr], start, len);
TRY(ds_push(fh, addr)); TRY(ds_push(fh, addr));
TRY(ds_push(fh, len)); TRY(ds_push(fh, len));
@ -653,7 +694,8 @@ enum fh_error fh_handle_quoted_string(
return FH_OK; return FH_OK;
} }
enum fh_error fh_handle_word( /** Process a word read from input */
static enum fh_error fh_handle_word(
struct fh_thread_s *fh, struct fh_thread_s *fh,
char *start, char *start,
size_t len size_t len
@ -700,37 +742,42 @@ enum fh_error fh_handle_word(
} }
if (fh->state == FH_STATE_COMPILE) { if (fh->state == FH_STATE_COMPILE) {
LOG("Compile number: %d", v); LOG("Compile number: %ld", v);
TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr)); TRY(fh_compile_reserve(fh, sizeof(struct fh_instruction_s), &addr));
instr.kind = FH_INSTR_NUMBER; instr.kind = FH_INSTR_NUMBER;
instr.data = (uint32_t) v; instr.data = (uint32_t) v;
memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s)); memcpy(&fh->compile[addr], &instr, sizeof(struct fh_instruction_s));
} else { } else {
/* interpret */ /* interpret */
LOG("Interpret number: %d", v); LOG("Interpret number: %ld", v);
TRY(ds_push(fh, (uint32_t) v)); TRY(ds_push(fh, (uint32_t) v));
} }
return FH_OK; return FH_OK;
} }
/** True if the character is whitespace */
static inline bool iswhite(char c) static inline bool iswhite(char c)
{ {
return c == ' ' || c == '\n' || c == '\t' || c == '\r'; return c == ' ' || c == '\n' || c == '\t' || c == '\r';
} }
/** True if the character is CR or LF */
static inline bool isnl(char c) static inline bool isnl(char c)
{ {
return c == '\n' || c == '\r'; return c == '\n' || c == '\r';
} }
enum fh_error fh_process_line(struct fh_thread_s *fh, char *linebuf) /** Process a line read from input */
static enum fh_error fh_process_line(struct fh_thread_s *fh, char *linebuf)
{ {
enum fh_error rv; enum fh_error rv;
char *rp = linebuf; char *rp = linebuf;
char c; char c;
LOGI("%s", linebuf); if (!fh_globals.interactive) {
LOGI("%s", linebuf);
}
while (0 != (c = *rp) && fh->state != FH_STATE_SHUTDOWN) { while (0 != (c = *rp) && fh->state != FH_STATE_SHUTDOWN) {
/* end on newline */ /* end on newline */
@ -806,6 +853,9 @@ enum fh_error fh_process_line(struct fh_thread_s *fh, char *linebuf)
case FH_SUBSTATE_LINECOMMENT: case FH_SUBSTATE_LINECOMMENT:
LOG("Discard line comment"); LOG("Discard line comment");
goto done; // just discard the rest goto done; // just discard the rest
default:
LOGE("Bad substate %s", substatenames[fh->substate]);
} }
} }
done: done:
@ -825,7 +875,7 @@ int main(int argc, char *argv[])
} }
fh_globals.verbose = false; fh_globals.verbose = false;
fh_globals.interactive = false; fh_globals.interactive = isatty(STDIN_FILENO);
FILE *infile = stdin; FILE *infile = stdin;
@ -835,7 +885,7 @@ int main(int argc, char *argv[])
// opt // opt
char *cc = argv[a] + 1; char *cc = argv[a] + 1;
char c; char c;
while(0 != (c = *cc++)) { while (0 != (c = *cc++)) {
switch (c) { switch (c) {
case 'v': case 'v':
fh_globals.verbose = 1; fh_globals.verbose = 1;
@ -876,7 +926,11 @@ int main(int argc, char *argv[])
FHPRINT_SVC(" ok\r\n"); FHPRINT_SVC(" ok\r\n");
} else { } else {
LOGE("ERROR %s on line %d", fherr_name(rv), linecnt); LOGE("ERROR %s on line %d", fherr_name(rv), linecnt);
return 1; if (!fh_globals.interactive) {
return 1;
}
/* reset state */
fh_setstate(&fh, FH_STATE_INTERPRET, FH_SUBSTATE_NONE);
} }
} }

Loading…
Cancel
Save