#ifndef TMPASM_H
#define TMPASM_H
#ifndef TMPASM_INSTR_MAXLEN
#define TMPASM_INSTR_MAXLEN 32
#endif
typedef struct tmpasm_s tmpasm_t;
typedef struct tmpasm_arg_s tmpasm_arg_t;
struct tmpasm_arg_s {
tmpasm_arg_t *next; /* block: the resulting string is a list of strings and addresses */
char is_addr; /* 1: arg is a node address; 0: arg is a string immediate */
char data[1]; /* arg string - obviously longer than 1 char (but there's not special hack for that in C89), \0 terminated */
};
/* user specified instruction prototype */
typedef void tmpasm_instr(tmpasm_t *ctx, char *iname, int argc, tmpasm_arg_t *argv[]);
typedef struct tmpasm_cb_s {
/* return the value of a node at addr - NULL is an error */
const char *(*get)(tmpasm_t *ctx, const char *addr);
/* set the value of a node at addr to data; data may be NULL */
void (*set)(tmpasm_t *ctx, const char *addr, const char *data);
/* return 1 if data is true, 0 otherwise; data may be NULL (if an unknown variable is referenced) */
int (*is_true)(tmpasm_t *ctx, const char *data);
/* return 1 if str matches pat, 0 otherwise; str and pat may be NULL */
int (*match)(tmpasm_t *ctx, const char *str, const char *pat);
/* first iteration over list; return the first element (or NULL to end); the string returned is not free'd by the caller */
const char *(*first)(tmpasm_t *ctx, void **state, char *list);
/* return next element of a list or NULL on end (in which case state shall be also free'd by the caller); the string returned is not free'd by the caller */
const char *(*next)(tmpasm_t *ctx, void **state);
/* resolve an instruction name to a function pointer */
tmpasm_instr *(*resolve)(tmpasm_t *ctx, const char *name);
/* optional: called once before execution of a context starts */
void (*preexec)(tmpasm_t *ctx);
/* optional: called once before execution of a context starts */
void (*postexec)(tmpasm_t *ctx);
/* optional: resolve the current runtime error, called only for negative
error codes; should return a format string with exactly one %s in it
or NULL. */
const char *(*runtime_error_fmt)(tmpasm_t *ctx);
} tmpasm_cb_t;
int tmpasm_gotchar(tmpasm_t *ctx, char c);
tmpasm_t *tmpasm_init(const tmpasm_cb_t *cb);
void tmpasm_uninit(tmpasm_t *ctx);
/* return the string version of an arg in a newly malloc()'d string
if keep_addr is non-zero and a is a single address, no get() is run
but the address is returned as a string */
char *tmpasm_arg2str(tmpasm_t *ctx, tmpasm_arg_t *a, int keep_addr);
/* execute the code recursively until it exits */
void tmpasm_execute(tmpasm_t *ctx);
/* Set or get the runtime error of a context. 0 means no error, negative
codes are user errors handled by the runtime_error_fmt() callback
and positive codes are internal error. */
void tmpasm_runtime_error(tmpasm_t *ctx, int code, const char *data);
const char *tmpasm_runtime_error_fmt(tmpasm_t *ctx);
/* --- internals: not required for normal use --- */
typedef enum {
ST_PRECMD, /* waiting for a command to start - ignore whitespace */
ST_CMD,
ST_PREDATA, /* waiting for data */
ST_PREBLOCKSEP, /* waiting for a block sep when opening a block */
ST_BLOCKSEP, /* found a block sep within the block - either an address or a termination follows */
ST_BLOCK, /* in [@ @] block, text part */
ST_BLOCK_INLINE, /* in [@ @] block, within inline @@ part */
ST_STRING, /* in {} string */
ST_STRING_ESCAPE, /* in {} string, right after a \ */
ST_ADDRESS, /* shifting address bytes */
ST_COMMENT /* after #, until the next newline */
} tmpasm_state_t;
typedef enum {
KW_none,
KW_IF,
KW_THEN,
KW_ELSE,
KW_END,
KW_FOREACH,
KW_IN,
KW_SWITCH,
KW_CASE,
KW_DEFAULT,
KW_NOP /* virtual instruction */
} tmpasm_kw_t;
/* execution structs */
typedef struct tmpasm_exec_s tmpasm_exec_t;
typedef struct tmpasm_case_s tmpasm_case_t;
struct tmpasm_case_s {
tmpasm_arg_t *data;
tmpasm_exec_t *body;
tmpasm_case_t *next;
};
struct tmpasm_exec_s {
tmpasm_kw_t kw; /* kw_none means a hook instruction */
union {
struct { /* normal instruction */
tmpasm_instr *call;
char *call_name; /* temporary */
int argc;
tmpasm_arg_t **argv;
} instr;
struct {
tmpasm_arg_t *cond;
tmpasm_exec_t *code_then;
tmpasm_exec_t *code_else;
} fc_if;
struct {
char *loop_var; /* must be a single address */
tmpasm_arg_t *data; /* what to loop in */
tmpasm_exec_t *code_body;
} fc_foreach;
struct {
tmpasm_arg_t *cond;
tmpasm_case_t *first;
tmpasm_case_t *last;
} fc_switch;
} payload;
int line, col;
tmpasm_exec_t *next;
};
/* parser structs */
typedef struct stack_s tmpasm_stack_t;
struct stack_s {
tmpasm_state_t state;
/* tmpasm_state_t kwstate; internal states of composite keywords like switch */
char cmd_buff[TMPASM_INSTR_MAXLEN+1];
unsigned int cmdi;
tmpasm_kw_t kw, old_kw;
char block_sep;
int kwcol, kwline;
int args_used, args_alloced; /* number of arguments in argv[] */
tmpasm_arg_t **argv; /* an array of linked lists */
tmpasm_arg_t **argend; /* each argv[] is a linked list (for blocks); argend points to the tail */
int *arg_alloced; /* of argend */
int *arg_used; /* of argend */
tmpasm_exec_t *last_code; /* tail of the code list */
tmpasm_stack_t *next;
};
struct tmpasm_s {
tmpasm_stack_t *st;
int dead;
int col, line;
tmpasm_exec_t *code;
tmpasm_exec_t *executing; /* points to the code most recently executed (or being executed when in callbacks) */
const tmpasm_cb_t *cb;
int halt;
int runtime_error;
char *runtime_error_data;
int runtime_error_line;
int runtime_error_col;
void *user_data;
};
#endif