aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFranklin Wei <git@fwei.tk>2015-11-10 17:19:33 -0500
committerFranklin Wei <git@fwei.tk>2015-11-10 17:19:33 -0500
commit1511952de93d7a85bb3b252d6ed47d434ff208f6 (patch)
tree83ace48d8dbeffa62050c847dfe3db10fd303c91 /src
parent768628ea1767b966418f21dced0572e222a6208b (diff)
downloadducky-1511952de93d7a85bb3b252d6ed47d434ff208f6.zip
ducky-1511952de93d7a85bb3b252d6ed47d434ff208f6.tar.gz
ducky-1511952de93d7a85bb3b252d6ed47d434ff208f6.tar.bz2
ducky-1511952de93d7a85bb3b252d6ed47d434ff208f6.tar.xz
add a rudimentary bytecode compiler
Diffstat (limited to 'src')
-rw-r--r--src/compile.c1274
-rw-r--r--src/ducky.h10
-rw-r--r--src/interp.c195
-rw-r--r--src/opcodes.h39
4 files changed, 1418 insertions, 100 deletions
diff --git a/src/compile.c b/src/compile.c
new file mode 100644
index 0000000..dd4703e
--- /dev/null
+++ b/src/compile.c
@@ -0,0 +1,1274 @@
+#include <ducky.h>
+#include <platform.h>
+
+#include "opcodes.h"
+
+/*******************************************************************************
+ * Bytecode format:
+ * { <INSTRUCTION> <ARGS> }...
+ * 0x00 - delay S[0] ms, pop
+ * 0x01 <A16> - mark as constant - vars[A].const = true
+ * 0x02 <A16> - push vars[A]
+ * 0x03 <B32> - push *B
+ * 0x04 <A16> - pop into vars[A]
+ * 0x05 - add the two numbers on top of the stack
+ * 0x06 - subtract the number on the top of the stack from the one below it
+ * 0x07 - multiply the two numbers on top of the stack
+ * 0x08 - S[1]/S[0]
+ * 0x09 - jump to S[0]; pop
+ * 0x0A - if S[1] == 0; JUMP S[0]; pop both
+ * 0x0B - eval S[1] ** S[0]
+ * 0x0C - eval S[1] && S[0]
+ * 0x0D - eval S[1] & S[0]
+ * 0x0E - eval S[1] << S[0]
+ * REPEAT: exec line s[0] s[1] times
+ */
+
+/*** Defines ***/
+
+#define DEFAULT_DELAY 0
+#define STRING_DELAY 0
+#define TOKEN_IS(str) (strcmp(tok, str) == 0)
+#define MAX_LINE_LEN 512
+
+#define MAXOPSTACK 64
+#define MAXNUMSTACK 64
+#define CALL_STACK_SZ 64
+#define VARMAP_SIZE 256
+
+#define VARFORMAT "%lld"
+#define VARNAME_MAX 24
+
+#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
+
+#define MIN(x,y) ((x<y)?(x):(y))
+
+typedef long long int vartype;
+
+/*** Globals ***/
+
+static off_t *line_offset = NULL;
+
+static unsigned lines_executed = 0, current_line = 0, num_lines;
+
+static unsigned call_stack[CALL_STACK_SZ];
+static unsigned stack_frame = 0;
+
+static int file_des = -1, out_fd = -1;
+
+struct var_t {
+ char name[VARNAME_MAX];
+ bool constant;
+};
+
+static void error(const char *fmt, ...) __attribute__((noreturn,format(print,1,2)));
+static void vid_write(const char *str);
+static void vid_logf(const char *fmt, ...) __attribute__((format(printf,1,2)));
+static void debug(const char *fmt, ...) __attribute__((format(printf,1,2)));
+static bool isValidVariable(const char *c);
+
+/* variables are stored in a chained hash map */
+/* collisions are manageable, but should be minimized */
+
+#define MAX_VARS 65336
+static struct var_t vars[MAX_VARS];
+
+void write_instr(instr_t ins)
+{
+ write(out_fd, &ins, sizeof(ins));
+ vid_logf("writing instruction 0x%x", ins);
+}
+
+void write_imm(imm_t imm)
+{
+ write(out_fd, &imm, sizeof(imm));
+ vid_logf("writing immediate 0x%x", imm);
+}
+
+void write_byte(unsigned char c)
+{
+ write(out_fd, &c, 1);
+ vid_logf("writing byte '%c'", c);
+}
+
+void write_varid(varid_t varid)
+{
+ write(out_fd, &varid, sizeof(varid));
+ vid_logf("writing varid %d which is %s", varid, vars[varid].name);
+}
+
+varid_t get_varid(const char *name)
+{
+ /* VERY VERY SLOW ALGORITHM, but it works */
+ static int last_assigned_var = 0;
+ for(int i = 0; i < last_assigned_var; ++i)
+ if(strcmp(name, vars[i].name) == 0)
+ return i;
+ strlcpy(vars[last_assigned_var].name, name, VARNAME_MAX);
+ ++last_assigned_var;
+ return last_assigned_var - 1;
+}
+
+static void setVariable(const char *name, vartype val)
+{
+ write_instr(PUSHIMM);
+ write_imm(val);
+ write_instr(POP);
+ write_varid(get_varid(name));
+}
+
+static void setConst(const char *name, bool c)
+{
+ if(c)
+ {
+ write_instr(MKCONST);
+ write_varid(get_varid(name));
+ }
+}
+
+static void incVar(const char *name)
+{
+ write_instr(INCVAR);
+ write_varid(get_varid(name));
+}
+
+static void decVar(const char *name)
+{
+ write_instr(DECVAR);
+ write_varid(get_varid(name));
+}
+
+/*** Utility functions ***/
+
+static void exit_handler(void)
+{
+ if(file_des >= 0)
+ close(file_des);
+ if(line_offset)
+ free(line_offset);
+}
+
+static void vid_write(const char *str)
+{
+ write_instr(WRITE_STR);
+ while(*str)
+ {
+ write_byte(*str++);
+ }
+ write_byte('\0');
+}
+
+static void __attribute__((format(printf,1,2))) vid_logf(const char *fmt, ...)
+{
+ char fmtbuf[256];
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap);
+ printf("%s\n", fmtbuf);
+ va_end(ap);
+}
+
+static void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...)
+{
+ char fmtbuf[256];
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap);
+ if(current_line)
+ vid_logf("Line %d: ", current_line);
+ vid_logf("ERROR: %s\n", fmtbuf);
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...)
+{
+ char fmtbuf[256];
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap);
+ vid_logf("Line %d: WARNING: %s\n", current_line, fmtbuf);
+ va_end(ap);
+}
+
+/* grabs a line from a file, -1 on error, returns # bytes read otherwise */
+static int read_line(int fd, char *buf, size_t sz)
+{
+ unsigned i = 0;
+ int bytes_read = 0;
+ int status = 1;
+ while(i < sz)
+ {
+ char c;
+ status = read(fd, &c, 1);
+ if(status != 1)
+ break;
+
+ ++bytes_read;
+
+ if(c == '\r')
+ continue;
+ if(c == '\n' || c == EOF)
+ {
+ break;
+ }
+
+ buf[i++] = c;
+ }
+ buf[MIN(i, sz - 1)] = '\0';
+
+ return (status <= 0)?-1:bytes_read;
+}
+
+/* index_lines() precalculates the offset of each line for faster jumping */
+/* also it does a quick pass to index all the labels */
+
+static off_t *index_lines(int fd, unsigned *numlines)
+{
+ size_t sz = sizeof(off_t);
+ off_t *data = malloc(sz);
+
+ /* this uses 1-indexed line numbers, so the first indice is wasted */
+ unsigned idx = 1;
+
+ while(1)
+ {
+ sz += sizeof(off_t);
+ data = realloc(data, sz);
+ data[idx] = lseek(fd, 0, SEEK_CUR);
+
+ char buf[MAX_LINE_LEN];
+
+ if(read_line(fd, buf, sizeof(buf)) < 0)
+ break;
+
+ char *save = NULL;
+ char *tok = strtok_r(buf, " \t", &save);
+ if(tok && (strcmp(tok, "LABEL") == 0 || strcmp("LBL", tok) == 0))
+ {
+ tok = strtok_r(NULL, " \t", &save);
+ if(tok && isValidVariable(tok))
+ {
+ setVariable(tok, idx);
+ setConst(tok, true);
+ }
+ }
+
+ ++idx;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ *numlines = idx - 1;
+
+ return data;
+}
+
+static void jump_line(int fd, unsigned where)
+{
+ if(1 <= where && where <= num_lines)
+ {
+ lseek(fd, line_offset[where], SEEK_SET);
+ }
+ else
+ error("JUMP target out of range (%u)", where);
+ current_line = where - 1;
+}
+
+static void sub_call(int fd, unsigned where)
+{
+ if(stack_frame < ARRAYLEN(call_stack))
+ {
+ call_stack[stack_frame] = current_line + 1;
+ ++stack_frame;
+ jump_line(fd, where);
+ }
+ else
+ error("call stack overflow");
+}
+
+static void sub_return(int fd)
+{
+ if(stack_frame > 0)
+ {
+ --stack_frame;
+ jump_line(fd, call_stack[stack_frame]);
+ }
+}
+
+/** Expression Parsing **/
+
+/* based on http://en.literateprograms.org/Shunting_yard_algorithm_%28C%29 */
+
+static void eval_uminus(void)
+{
+ write_instr(NEG);
+}
+static void eval_exp(void)
+{
+ write_instr(POW);
+}
+static void eval_mul(void)
+{
+ write_instr(MULT);
+}
+static void eval_div(void)
+{
+ write_instr(DIV);
+}
+static void eval_mod(void)
+{
+ write_instr(MOD);
+}
+static void eval_add(void)
+{
+ write_instr(ADD);
+}
+static void eval_sub(void)
+{
+ write_instr(SUB);
+}
+static void eval_eq(void)
+{
+ write_instr(EQ);
+}
+static void eval_neq(void)
+{
+ write_instr(NEQ);
+}
+static void eval_leq(void)
+{
+ write_instr(LEQ);
+}
+static void eval_geq(void)
+{
+ write_instr(GEQ);
+}
+static void eval_lt(void)
+{
+ write_instr(LT);
+}
+static void eval_gt(void)
+{
+ write_instr(GT);
+}
+static void eval_log_neg(void)
+{
+ write_instr(LOGNOT);
+}
+static void eval_log_and(void)
+{
+ write_instr(LOGAND);
+}
+static void eval_log_or(void)
+{
+ write_instr(LOGOR);
+}
+static void eval_bit_and(void)
+{
+ write_instr(BITAND);
+}
+static void eval_bit_xor(void)
+{
+ write_instr(BITXOR);
+}
+static void eval_bit_or(void)
+{
+ write_instr(BITOR);
+}
+static void eval_bit_comp(void)
+{
+ write_instr(BITCOMP);
+}
+static void eval_lsh(void)
+{
+ write_instr(LSH);
+}
+static void eval_rsh(void)
+{
+ write_instr(RSH);
+}
+static void eval_sqrt(void)
+{
+ write_instr(SQRT);
+}
+
+enum {ASSOC_NONE=0, ASSOC_LEFT, ASSOC_RIGHT};
+
+/* order matters in this table, because operators can share prefixes */
+/* apart from that, they should be ordered by frequency of use */
+/* operator precedence is based on that of C */
+/* frequency is based off a crude analysis of the rockbox source tree: */
+
+/* 99639 * */
+/* 48282 - */
+/* 46639 + */
+/* 27678 & */
+/* 24542 < */
+/* 21862 / */
+/* 20000 | */
+/* 19138 == */
+/* 12694 % */
+/* 11619 > */
+/* 11087 ! */
+/* 8230 << */
+/* 7339 && */
+/* 7180 != */
+/* 6010 >> */
+/* 5575 || */
+/* 3121 ~ */
+/* 1311 ^ */
+
+/* arrays are implemented as UNARY OPERATORS */
+
+static struct op_s {
+ const char *op;
+ int prec;
+ int assoc;
+ int unary;
+ void (*eval)(void);
+ unsigned int len;
+} ops[] = {
+ {"+", 20, ASSOC_LEFT, 0, eval_add, -1},
+ {"-", 20, ASSOC_LEFT, 0, eval_sub, -1},
+ {"**", 40, ASSOC_RIGHT, 0, eval_exp, -1},
+ {"*", 30, ASSOC_LEFT, 0, eval_mul, -1},
+ {"&&", 8, ASSOC_LEFT, 0, eval_log_and, -1},
+ {"&", 11, ASSOC_LEFT, 0, eval_bit_and, -1},
+ {"<<", 15, ASSOC_LEFT, 0, eval_lsh, -1},
+ {">>", 15, ASSOC_LEFT, 0, eval_rsh, -1},
+ {"<=", 14, ASSOC_LEFT, 0, eval_leq, -1},
+ {">=", 14, ASSOC_LEFT, 0, eval_geq, -1},
+ {"<", 14, ASSOC_LEFT, 0, eval_lt, -1},
+ {">", 14, ASSOC_LEFT, 0, eval_gt, -1},
+ {"/", 30, ASSOC_LEFT, 0, eval_div, -1},
+ {"||", 7, ASSOC_LEFT, 0, eval_log_or, -1},
+ {"|", 9, ASSOC_LEFT, 0, eval_bit_or, -1},
+ {"==", 12, ASSOC_LEFT, 0, eval_eq, -1},
+ {"!=", 12, ASSOC_LEFT, 0, eval_neq, -1},
+ {"%", 30, ASSOC_LEFT, 0, eval_mod, -1},
+ {"!", 50, ASSOC_LEFT, 1, eval_log_neg, -1},
+ {"~", 50, ASSOC_LEFT, 1, eval_bit_comp, -1},
+ {"^", 10, ASSOC_LEFT, 0, eval_bit_xor, -1},
+ {"(", 0, ASSOC_NONE, 0, NULL, -1},
+ {")", 0, ASSOC_NONE, 0, NULL, -1},
+ {"sqrt", 1, ASSOC_LEFT, 1, eval_sqrt, -1},
+};
+
+#define OPMAP_SIZE 25
+
+static void op_hash_round(char c, uint32_t *hash)
+{
+ *hash *= 70;
+ *hash ^= c;
+}
+
+static uint32_t op_hash(const char *c)
+{
+ uint32_t hash = 4412;
+ while(1)
+ {
+ if(!*c)
+ return hash;
+ op_hash_round(*c, &hash);
+ ++c;
+ }
+}
+
+/* optimized hash map for fast lookup of operators */
+static struct op_s op_map[OPMAP_SIZE];
+static size_t longest_op = 0;
+
+static void opmap_insert(struct op_s *op)
+{
+ if(op->len > longest_op)
+ longest_op = op->len;
+
+ uint32_t hash = op_hash(op->op) % OPMAP_SIZE;
+
+ if(op_map[hash].op)
+ error("hash map collision %lu: %s VS %s", hash, op->op, op_map[hash].op);
+ memcpy(op_map+hash, op, sizeof(*op));
+}
+
+static void init_optable(void)
+{
+ memset(op_map, 0, sizeof(op_map));
+ for(unsigned int i = 0; i < ARRAYLEN(ops); ++i)
+ {
+ ops[i].len = strlen(ops[i].op);
+ opmap_insert(ops+i);
+ }
+}
+
+static const struct op_s *getop(const char *ch, int *len)
+{
+ unsigned int i = 0;
+ uint32_t hash = 4412;
+ const struct op_s *poss = NULL;
+ do {
+ op_hash_round(ch[i], &hash);
+ uint32_t modhash = hash % OPMAP_SIZE;
+
+ if(op_map[modhash].op && strncmp(ch, op_map[modhash].op, op_map[modhash].len) == 0)
+ {
+ *len = op_map[modhash].len;
+ poss = op_map + modhash;
+ }
+ } while(ch[i++] && i < longest_op);
+ return poss;
+}
+
+static const struct op_s *opstack[MAXOPSTACK];
+static int nopstack;
+
+static vartype numstack[MAXNUMSTACK];
+static int nnumstack;
+
+static void push_opstack(const struct op_s *op)
+{
+ if(nopstack>MAXOPSTACK - 1) {
+ error("operator stack overflow");
+ }
+ opstack[nopstack++] = op;
+}
+
+static const struct op_s *pop_opstack(void)
+{
+ if(!nopstack) {
+ error("operator stack empty");
+ }
+ return opstack[--nopstack];
+}
+
+static void push_numstack(vartype num)
+{
+ if(nnumstack>MAXNUMSTACK - 1) {
+ error("number stack overflow");
+ }
+ numstack[nnumstack++] = num;
+}
+
+static vartype pop_numstack(void)
+{
+ if(!nnumstack) {
+ error("number stack empty");
+ }
+ return numstack[--nnumstack];
+}
+
+static bool isDigit(char c)
+{
+ return '0' <= c && c <= '9';
+}
+
+static bool isValidNumber(char *str)
+{
+ //vid_logf("isValidNumber %s", str);
+ if(str && (isDigit(*str) || *str == '-'))
+ {
+ while(1)
+ {
+ char c = *str++;
+ if(!c)
+ break;
+ if(!isDigit(c))
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+static bool isSpace(char c)
+{
+ //vid_logf("isSpace '%c'", c);
+ return (c == ' ') || (c == '\t');
+}
+
+static bool isValidVariable(const char *c)
+{
+ //vid_logf("isValidVariable %s", c);
+ if(!isDigit(*c) && !getop(c, NULL) && !isSpace(*c))
+ {
+ return true;
+ }
+ return false;
+}
+
+static bool isValidNumberOrVariable(const char *c)
+{
+ //vid_logf("isValidNumberOrVariable %s", c);
+ if(isDigit(*c) || isValidVariable(c))
+ {
+ return true;
+ }
+ return false;
+}
+
+static void shunt_op(const struct op_s *op)
+{
+ const struct op_s *pop;
+ vartype n1, n2;
+ if(strcmp(op->op, "(") == 0)
+ {
+ push_opstack(op);
+ return;
+ }
+ else if(strcmp(op->op, ")") == 0)
+ {
+ while(nopstack > 0 && strcmp(opstack[nopstack-1]->op, "(") != 0)
+ {
+ pop = pop_opstack();
+ pop->eval();
+ }
+
+ if(!(pop = pop_opstack()) || strcmp(pop->op,"(") != 0)
+ {
+ error("mismatched parentheses");
+ }
+ return;
+ }
+
+ if(op->assoc == ASSOC_LEFT)
+ {
+ while(nopstack && op->prec <= opstack[nopstack - 1]->prec)
+ {
+ pop = pop_opstack();
+ pop->eval();
+ }
+ }
+ else
+ {
+ while(nopstack && op->prec<opstack[nopstack - 1]->prec)
+ {
+ pop = pop_opstack();
+ pop->eval();
+ }
+ }
+
+ push_opstack(op);
+}
+
+static vartype eval_expr(char *str)
+{
+ //vid_write("**************** EVAL EXPR ***************");
+
+ /* token start */
+ char *tstart = NULL;
+
+ /* hard-code some operators that are for internal use only */
+ const struct op_s startop = {"startop_", 0, ASSOC_NONE, 0, NULL, strlen("startop_")};
+ const struct op_s unaryminus = {"-", 50, ASSOC_RIGHT, 1, eval_uminus, strlen("-")};
+
+ const struct op_s *op = NULL;
+ vartype n1, n2;
+ const struct op_s *lastop = &startop;
+
+ int len;
+ char *expr;
+ for(expr = str; *expr; expr += len)
+ {
+ //vid_write("****** PARSING A CHARACTER ******");
+ len = 1;
+ if(!tstart)
+ {
+ if((op = getop(expr, &len)))
+ {
+ if(lastop && (lastop == &startop || strcmp(lastop->op, ")") != 0))
+ {
+ if(strcmp(op->op, "-") == 0)
+ {
+ op = &unaryminus;
+ len = 1;
+ }
+ else if(strcmp(op->op, "(") != 0 && !op->unary)
+ {
+ error("illegal use of binary operator (%s)", op->op);
+ }
+ }
+ shunt_op(op);
+ lastop = op;
+ }
+ else if(isValidNumberOrVariable(expr))
+ tstart = expr;
+ else if(!isSpace(*expr))
+ {
+ error("syntax error");
+ }
+ }
+ else
+ {
+ if(isSpace(*expr))
+ {
+ //push_numstack(getValue(tstart, expr));
+
+ if(isValidVariable(tstart))
+ {
+ write_instr(PUSHVAR);
+
+ /* isolate the variable name into a buffer */
+ char varname[VARNAME_MAX + 1] = { 0 };
+ memcpy(varname, str, expr - tstart);
+ write_varid(get_varid(varname));
+ }
+ else
+ {
+ write_instr(PUSHIMM);
+ write_imm(strtol(tstart, NULL, 0));
+ }
+ tstart = NULL;
+ lastop = NULL;
+ }
+ else if((op = getop(expr, &len)))
+ {
+ //push_numstack(getValue(tstart, expr));
+ if(isValidVariable(tstart))
+ {
+ write_instr(PUSHVAR);
+
+ /* isolate the variable name into a buffer */
+ char varname[VARNAME_MAX + 1] = { 0 };
+ memcpy(varname, str, expr - tstart);
+ write_varid(get_varid(varname));
+ }
+ else
+ {
+ write_instr(PUSHIMM);
+ write_imm(strtol(tstart, NULL, 0));
+ }
+
+ tstart = NULL;
+ shunt_op(op);
+ lastop = op;
+ }
+ else if(!isValidNumberOrVariable(expr))
+ {
+ error("syntax error");
+ }
+ }
+ }
+
+ if(tstart)
+ {
+ //push_numstack(getValue(tstart, expr));
+ if(isValidVariable(tstart))
+ {
+ write_instr(PUSHVAR);
+
+ /* isolate the variable name into a buffer */
+ char varname[VARNAME_MAX + 1] = { 0 };
+ memcpy(varname, str, expr - tstart);
+ write_varid(get_varid(varname));
+ }
+ else
+ {
+ write_instr(PUSHIMM);
+ write_imm(strtol(tstart, NULL, 0));
+ }
+ }
+
+ while(nopstack) {
+ op = pop_opstack();
+ op->eval();
+ }
+}
+
+#define OK 0
+#define DONE 1
+#define NEXT 2
+#define BREAK 3
+
+static int let_handler(char **save)
+{
+ (void) save;
+ char *varname = strtok_r(NULL, "= \t", save);
+
+ if(varname && isValidVariable(varname))
+ {
+ int varid = get_varid(varname);
+ char *tok = strtok_r(NULL, "=;", save);
+ if(tok)
+ eval_expr(tok);
+ else
+ error("exprected valid expression after LET");
+
+ write_instr(POP);
+ write_varid(varid);
+ }
+ else
+ {
+ error("invalid variable name for LET");
+ }
+ return OK;
+}
+
+static int repeat_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, ";", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(PUSHIMM);
+ write_imm(current_line - 1);
+ write_instr(REPEAT);
+ return OK;
+ }
+ else
+ error("expected valid expression after REPEAT");
+}
+
+static int goto_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, ";", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(JUMP);
+ return OK;
+ }
+ else
+ error("expected valid expression after GOTO");
+}
+
+static int call_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, "", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(SUBCALL);
+ //sub_call(file_des, eval_expr(tok));
+ return OK;
+ }
+ else
+ error("expected destination for CALL");
+}
+
+static int ret_handler(char **save)
+{
+ (void) save;
+ write_instr(SUBRET);
+ //sub_return(file_des);
+ return NEXT;
+}
+
+static int inc_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, " \t", save);
+
+ if(isValidVariable(tok))
+ incVar(tok);
+ return OK;
+}
+
+static int dec_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, " \t", save);
+
+ if(isValidVariable(tok))
+ decVar(tok);
+ return OK;
+}
+
+static int if_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, ";", save);
+
+ if(!tok)
+ error("expected conditional after IF");
+
+ eval_expr(tok);
+ write_instr(PUSHIMM);
+ write_imm(current_line + 1);
+ write_instr(IF);
+
+ return OK;
+}
+
+static int delay_handler(char **save)
+{
+ (void) save;
+ /* delay N 1000ths of a sec */
+ char *tok = strtok_r(NULL, ";", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(DELAY);
+ }
+ else
+ error("expected valid expression after DELAY");
+
+ return OK;
+}
+
+static int log_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, "", save);
+
+ vid_write(tok);
+ return OK;
+}
+
+static int logvar_handler(char **save)
+{
+ (void) save;
+ char *tok = strtok_r(NULL, ";", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(LOGVAR);
+ return OK;
+ }
+ else
+ error("expected expression after LOGVAR");
+}
+
+static int rem_handler(char **save)
+{
+ (void) save;
+ return BREAK;
+}
+
+static int quit_handler(char **save)
+{
+ (void) save;
+ write_instr(QUIT);
+ return OK;
+}
+
+static int newline_handler(char **save)
+{
+ (void) save;
+ vid_write("\n");
+ return OK;
+}
+
+static int logchar_handler(char **save)
+{
+ char *tok = strtok_r(NULL, ";", save);
+ if(tok)
+ {
+ eval_expr(tok);
+ write_instr(LOGASCII);
+ return OK;
+ }
+ else
+ error("expected expression after LOGCHAR");
+
+}
+
+static struct token_t {
+ const char *tok;
+ int (*func)(char **save);
+} tokens[] = {
+ { "LET", let_handler, },
+ { "REPEAT", repeat_handler, },
+ { "JUMP", goto_handler, },
+ { "GOTO", goto_handler, },
+ { "CALL", call_handler, },
+ { "GOSUB", call_handler, },
+ { "RET", ret_handler, },
+ { "RETURN", ret_handler, },
+ { "INC", inc_handler, },
+ { "DEC", dec_handler, },
+ { "IF", if_handler, },
+ { "DELAY", delay_handler, },
+ { "LOG", log_handler, },
+ { "LOGVAR", logvar_handler, },
+ { "LOGCHAR", logchar_handler },
+ { "NEWLINE", newline_handler, },
+ { "REM", rem_handler, },
+ { "//", rem_handler, },
+ { "LABEL", rem_handler, },
+ { "LBL", rem_handler, },
+ { "QUIT", quit_handler, },
+ { "EXIT", quit_handler, },
+};
+
+/* once again, this lookup table is implemented with a perfect hash map */
+
+#define TOKMAP_SIZE ARRAYLEN(tokens)
+static struct token_t tokmap[TOKMAP_SIZE];
+
+/* auto-generated with mph-1.2 */
+/*
+ * d=3
+ * n=28
+ * m=22
+ * c=1.23
+ * maxlen=7
+ * minklen=2
+ * maxklen=7
+ * minchar=47
+ * maxchar=89
+ * loop=0
+ * numiter=200
+ * seed=
+ */
+
+static int g[] = {
+ 19, 12, -1, 15, 17, 1, 4, 4, 0, 13,
+ 10, 19, 18, -1, 14, 19, 19, 21, 19, 0,
+ 7, 0, 15, 18, 4, 0, 4, 3,
+};
+
+static int T0[] = {
+ 0x15, 0x0c, 0x1a, 0x17, 0x16, 0x02, 0x16, 0x07, 0x17, 0x09,
+ 0x0f, 0x0f, 0x0f, 0x17, 0x07, 0x0c, 0x1a, 0x08, 0x17, 0x04,
+ 0x14, 0x07, 0x06, 0x07, 0x10, 0x0e, 0x0e, 0x18, 0x01, 0x08,
+ 0x1b, 0x17, 0x14, 0x19, 0x12, 0x1a, 0x0b, 0x0c, 0x12, 0x07,
+ 0x05, 0x05, 0x06, 0x13, 0x06, 0x19, 0x07, 0x0d, 0x01, 0x0c,
+ 0x1a, 0x09, 0x15, 0x1b, 0x16, 0x03, 0x15, 0x19, 0x12, 0x07,
+ 0x1b, 0x05, 0x08, 0x0a, 0x15, 0x05, 0x16, 0x0b, 0x0a, 0x00,
+ 0x0e, 0x0d, 0x08, 0x0a, 0x04, 0x1b, 0x07, 0x17, 0x0c, 0x15,
+ 0x07, 0x0a, 0x02, 0x0c, 0x15, 0x08, 0x0d, 0x12, 0x10, 0x00,
+ 0x07, 0x13, 0x15, 0x10, 0x17, 0x0a, 0x0e, 0x01, 0x14, 0x14,
+ 0x1a, 0x06, 0x15, 0x05, 0x0a, 0x14, 0x03, 0x12, 0x04, 0x0c,
+ 0x11, 0x1b, 0x0e, 0x1b, 0x17, 0x03, 0x08, 0x09, 0x15, 0x08,
+ 0x15, 0x0c, 0x1b, 0x0e, 0x00, 0x16, 0x08, 0x1b, 0x08, 0x10,
+ 0x0a, 0x00, 0x09, 0x01, 0x00, 0x09, 0x13, 0x03, 0x19, 0x03,
+ 0x05, 0x15, 0x10, 0x06, 0x13, 0x02, 0x19, 0x15, 0x13, 0x12,
+ 0x18, 0x16, 0x02, 0x0b, 0x0a, 0x1b, 0x0a, 0x15, 0x08, 0x06,
+ 0x09, 0x12, 0x06, 0x12, 0x14, 0x12, 0x0c, 0x17, 0x15, 0x09,
+ 0x1a, 0x0b, 0x05, 0x00, 0x1b, 0x0c, 0x10, 0x09, 0x02, 0x17,
+ 0x11, 0x0a, 0x15, 0x11, 0x18, 0x12, 0x16, 0x00, 0x0e, 0x10,
+ 0x18, 0x11, 0x0b, 0x07, 0x0b, 0x1a, 0x1b, 0x07, 0x07, 0x15,
+ 0x0a, 0x16, 0x1a, 0x0f, 0x06, 0x19, 0x0c, 0x17, 0x06, 0x0e,
+ 0x12, 0x08, 0x18, 0x17, 0x09, 0x0c, 0x03, 0x1b, 0x15, 0x0e,
+ 0x06, 0x01, 0x11, 0x0e, 0x04, 0x08, 0x05, 0x1b, 0x05, 0x03,
+ 0x15, 0x04, 0x17, 0x10, 0x0e, 0x07, 0x0e, 0x14, 0x13, 0x17,
+ 0x14, 0x1b, 0x0a, 0x07, 0x18, 0x04, 0x03, 0x00, 0x10, 0x19,
+ 0x1a, 0x16, 0x0a, 0x0f, 0x14, 0x1a, 0x18, 0x19, 0x0c, 0x1b,
+ 0x06, 0x0a, 0x03, 0x0b, 0x0e, 0x1b, 0x1b, 0x0d, 0x03, 0x0d,
+ 0x19, 0x0a, 0x0f, 0x14, 0x14, 0x06, 0x00, 0x01, 0x10, 0x0d,
+ 0x19, 0x18, 0x09, 0x1b, 0x17, 0x16, 0x14, 0x15, 0x0b, 0x10,
+ 0x14, 0x02, 0x1b, 0x08, 0x0d, 0x0d, 0x13, 0x0c, 0x0a, 0x16,
+ 0x09,
+};
+
+static int T1[] = {
+ 0x13, 0x0e, 0x17, 0x12, 0x13, 0x12, 0x08, 0x19, 0x05, 0x0d,
+ 0x0b, 0x07, 0x06, 0x05, 0x0b, 0x0f, 0x06, 0x07, 0x12, 0x06,
+ 0x11, 0x0d, 0x08, 0x10, 0x18, 0x1b, 0x18, 0x12, 0x03, 0x00,
+ 0x00, 0x16, 0x1a, 0x07, 0x18, 0x11, 0x19, 0x10, 0x1a, 0x0e,
+ 0x0e, 0x16, 0x16, 0x02, 0x10, 0x0d, 0x11, 0x0a, 0x02, 0x14,
+ 0x00, 0x0c, 0x1a, 0x1a, 0x08, 0x02, 0x01, 0x06, 0x0b, 0x0b,
+ 0x06, 0x02, 0x13, 0x15, 0x0f, 0x11, 0x0d, 0x01, 0x04, 0x0c,
+ 0x0b, 0x13, 0x02, 0x11, 0x06, 0x12, 0x0e, 0x07, 0x01, 0x10,
+ 0x1b, 0x01, 0x01, 0x0a, 0x0b, 0x09, 0x0c, 0x13, 0x11, 0x0a,
+ 0x03, 0x11, 0x02, 0x13, 0x01, 0x08, 0x0c, 0x0a, 0x06, 0x0e,
+ 0x00, 0x13, 0x1b, 0x03, 0x12, 0x01, 0x01, 0x0e, 0x0d, 0x09,
+ 0x11, 0x1b, 0x08, 0x05, 0x0b, 0x0c, 0x02, 0x08, 0x10, 0x13,
+ 0x02, 0x13, 0x14, 0x04, 0x16, 0x15, 0x18, 0x06, 0x10, 0x13,
+ 0x1a, 0x01, 0x03, 0x16, 0x17, 0x12, 0x1b, 0x10, 0x0f, 0x0f,
+ 0x06, 0x0f, 0x0f, 0x1a, 0x10, 0x17, 0x18, 0x09, 0x06, 0x10,
+ 0x01, 0x03, 0x06, 0x0d, 0x03, 0x0d, 0x0d, 0x0f, 0x16, 0x09,
+ 0x13, 0x14, 0x0b, 0x16, 0x1a, 0x12, 0x0d, 0x1a, 0x06, 0x00,
+ 0x19, 0x18, 0x17, 0x18, 0x1b, 0x10, 0x0d, 0x14, 0x17, 0x16,
+ 0x0a, 0x04, 0x0e, 0x03, 0x10, 0x1a, 0x01, 0x10, 0x19, 0x04,
+ 0x09, 0x0f, 0x08, 0x0b, 0x1a, 0x0f, 0x0f, 0x09, 0x09, 0x1b,
+ 0x18, 0x08, 0x16, 0x03, 0x10, 0x05, 0x14, 0x02, 0x19, 0x0f,
+ 0x18, 0x13, 0x03, 0x16, 0x06, 0x1b, 0x01, 0x0f, 0x19, 0x0d,
+ 0x00, 0x0a, 0x11, 0x0f, 0x0d, 0x0e, 0x08, 0x10, 0x1b, 0x0c,
+ 0x1b, 0x19, 0x08, 0x17, 0x0c, 0x1b, 0x0a, 0x12, 0x0d, 0x0f,
+ 0x0a, 0x14, 0x04, 0x0f, 0x0b, 0x05, 0x0f, 0x18, 0x04, 0x18,
+ 0x09, 0x05, 0x06, 0x1b, 0x04, 0x13, 0x19, 0x0c, 0x1b, 0x0c,
+ 0x18, 0x19, 0x08, 0x0e, 0x11, 0x0b, 0x03, 0x16, 0x1b, 0x15,
+ 0x11, 0x14, 0x09, 0x09, 0x17, 0x0e, 0x12, 0x1a, 0x14, 0x12,
+ 0x19, 0x08, 0x16, 0x07, 0x12, 0x0a, 0x17, 0x14, 0x13, 0x06,
+ 0x10, 0x0f, 0x03, 0x18, 0x0d, 0x04, 0x13, 0x10, 0x1b, 0x03,
+ 0x09,
+};
+
+static int T2[] = {
+ 0x16, 0x04, 0x18, 0x10, 0x13, 0x0f, 0x08, 0x19, 0x19, 0x17,
+ 0x13, 0x0b, 0x0b, 0x08, 0x0a, 0x08, 0x01, 0x05, 0x15, 0x1a,
+ 0x11, 0x02, 0x16, 0x0f, 0x0d, 0x09, 0x16, 0x13, 0x17, 0x0d,
+ 0x05, 0x11, 0x11, 0x0d, 0x05, 0x14, 0x01, 0x19, 0x12, 0x0a,
+ 0x15, 0x15, 0x15, 0x17, 0x13, 0x15, 0x0e, 0x18, 0x11, 0x0f,
+ 0x03, 0x04, 0x03, 0x09, 0x1a, 0x13, 0x04, 0x08, 0x09, 0x07,
+ 0x15, 0x16, 0x05, 0x11, 0x02, 0x00, 0x15, 0x10, 0x1b, 0x0d,
+ 0x14, 0x18, 0x10, 0x0f, 0x13, 0x13, 0x14, 0x11, 0x10, 0x0a,
+ 0x05, 0x03, 0x1a, 0x14, 0x0d, 0x19, 0x05, 0x1a, 0x1a, 0x00,
+ 0x06, 0x0c, 0x07, 0x11, 0x01, 0x18, 0x12, 0x06, 0x02, 0x06,
+ 0x1a, 0x17, 0x03, 0x0d, 0x09, 0x02, 0x09, 0x19, 0x03, 0x01,
+ 0x16, 0x0d, 0x17, 0x10, 0x01, 0x06, 0x18, 0x06, 0x10, 0x16,
+ 0x07, 0x06, 0x12, 0x1a, 0x17, 0x04, 0x16, 0x19, 0x0a, 0x1b,
+ 0x15, 0x16, 0x13, 0x11, 0x09, 0x1b, 0x02, 0x05, 0x12, 0x0c,
+ 0x10, 0x11, 0x08, 0x0f, 0x0c, 0x05, 0x0d, 0x0f, 0x19, 0x01,
+ 0x1b, 0x17, 0x03, 0x08, 0x00, 0x1a, 0x0d, 0x0c, 0x07, 0x19,
+ 0x17, 0x01, 0x03, 0x0e, 0x02, 0x18, 0x19, 0x10, 0x02, 0x00,
+ 0x0c, 0x02, 0x0a, 0x0b, 0x1a, 0x11, 0x19, 0x01, 0x16, 0x02,
+ 0x09, 0x01, 0x10, 0x1a, 0x0c, 0x0c, 0x12, 0x10, 0x18, 0x1b,
+ 0x14, 0x17, 0x17, 0x12, 0x1b, 0x08, 0x0d, 0x0c, 0x07, 0x1a,
+ 0x04, 0x03, 0x0b, 0x0e, 0x1b, 0x16, 0x10, 0x08, 0x17, 0x16,
+ 0x16, 0x10, 0x17, 0x0a, 0x1a, 0x0c, 0x17, 0x0f, 0x0a, 0x19,
+ 0x00, 0x0c, 0x08, 0x11, 0x17, 0x09, 0x17, 0x0f, 0x00, 0x05,
+ 0x17, 0x05, 0x05, 0x17, 0x17, 0x15, 0x0f, 0x00, 0x00, 0x19,
+ 0x0c, 0x19, 0x0f, 0x11, 0x08, 0x18, 0x01, 0x10, 0x17, 0x17,
+ 0x0d, 0x18, 0x13, 0x15, 0x0d, 0x0e, 0x0e, 0x09, 0x05, 0x17,
+ 0x14, 0x17, 0x16, 0x16, 0x1b, 0x10, 0x10, 0x0c, 0x06, 0x02,
+ 0x17, 0x12, 0x05, 0x08, 0x12, 0x00, 0x0b, 0x17, 0x07, 0x12,
+ 0x08, 0x01, 0x18, 0x0a, 0x19, 0x16, 0x01, 0x0b, 0x12, 0x12,
+ 0x06, 0x0a, 0x0e, 0x0c, 0x10, 0x0d, 0x00, 0x04, 0x19, 0x12,
+ 0x06,
+};
+
+#define uchar unsigned char
+
+static int
+tok_hash(const uchar *key)
+{
+ int i;
+ unsigned f0, f1, f2;
+ const uchar *kp = key;
+
+ for (i=-47, f0=f1=f2=0; *kp; ++kp) {
+ if (*kp < 47 || *kp > 89)
+ return -1;
+ if (kp-key > 6)
+ return -1;
+ f0 += T0[i + *kp];
+ f1 += T1[i + *kp];
+ f2 += T2[i + *kp];
+ i += 43;
+ }
+
+ if (kp-key < 2)
+ return -1;
+
+ f0 %= 28;
+ f1 %= 28;
+ f2 %= 28;
+
+ return (g[f0] + g[f1] + g[f2]) % 22;
+}
+
+static void tokmap_insert(struct token_t *tok)
+{
+ uint32_t hash = tok_hash(tok->tok) % TOKMAP_SIZE;
+ if(hash < 0 || tokmap[hash].tok)
+ error("FIXME: hash map collision");
+ memcpy(tokmap+hash, tok, sizeof(*tok));
+}
+
+static void init_tokmap(void)
+{
+ memset(tokmap, 0, sizeof(tokmap));
+ for(unsigned int i = 0; i < ARRAYLEN(tokens); ++i)
+ {
+ tokmap_insert(tokens+i);
+ }
+}
+
+static void init_globals(void)
+{
+ line_offset = NULL;
+ file_des = -1;
+ stack_frame = 0;
+ current_line = 0;
+}
+
+void ducky_compile(int fd, bool verbose, int out)
+{
+ init_globals();
+
+ if(verbose)
+ {
+ vid_logf("COMPILER INIT");
+ }
+
+ file_des = fd;
+ out_fd = out;
+
+ atexit(exit_handler);
+
+ if(file_des < 0)
+ error("invalid file");
+
+ init_optable();
+ init_tokmap();
+
+ /* initialize some other constants */
+ //setVariable("true", 1);
+ //setConst("true", true);
+
+ //setVariable("false", 0);
+ //setConst("false", true);
+
+ line_offset = index_lines(file_des, &num_lines);
+ if(verbose)
+ {
+ vid_logf("Indexing complete (%u lines).", num_lines);
+ vid_logf("Compiling...");
+ }
+ int repeats_left = 0;
+
+ while(1)
+ {
+ char instr_buf[MAX_LINE_LEN];
+ memset(instr_buf, 0, sizeof(instr_buf));
+ if(read_line(file_des, instr_buf, sizeof(instr_buf)) <= 0)
+ {
+ if(verbose)
+ vid_logf("end of file");
+ goto done;
+ }
+ char *tok = NULL, *save = NULL;
+
+ ++current_line;
+
+ char *buf = instr_buf;
+
+ /* execute all the commands on this line/instruction */
+ do {
+ tok = strtok_r(buf, " -\t", &save);
+ buf = NULL;
+
+ if(!tok)
+ break;
+
+ int hash = tok_hash(tok) % TOKMAP_SIZE;
+ struct token_t *t = tokmap+hash;
+ if(hash >= 0 && strcmp(t->tok, tok) == 0)
+ switch(tokmap[hash].func(&save))
+ {
+ case OK:
+ break;
+ case BREAK:
+ goto break_out;
+ case DONE:
+ goto done;
+ case NEXT:
+ goto next_line;
+ default:
+ error("FIXME: invalid return value");
+ }
+ else if(tok[0] != '#')
+ {
+ error("unknown token `%s` on line %d %d", tok, current_line);
+ goto done;
+ }
+ } while(tok);
+ break_out:
+ ;
+ next_line:
+ ;
+ }
+
+done:
+
+ return;
+}
diff --git a/src/ducky.h b/src/ducky.h
index 03c24d7..b92b96c 100644
--- a/src/ducky.h
+++ b/src/ducky.h
@@ -1 +1,9 @@
-void ducky_main(int fd);
+#include <stdbool.h>
+#include <stdint.h>
+
+void ducky_main(int fd, bool verbose);
+void ducky_compile(int fd, bool verbose, int out_fd);
+
+typedef int32_t imm_t;
+typedef uint8_t instr_t;
+typedef uint16_t varid_t;
diff --git a/src/interp.c b/src/interp.c
index e3b00b6..0d809dc 100644
--- a/src/interp.c
+++ b/src/interp.c
@@ -48,14 +48,14 @@ typedef long long int vartype;
/*** Globals ***/
-off_t *line_offset = NULL;
+static off_t *line_offset = NULL;
-unsigned lines_executed = 0, current_line = 0, num_lines;
+static unsigned lines_executed = 0, current_line = 0, num_lines;
-unsigned call_stack[CALL_STACK_SZ];
-unsigned stack_frame = 0;
+static unsigned call_stack[CALL_STACK_SZ];
+static unsigned stack_frame = 0;
-int log_fd = -1, file_des = -1;
+static int file_des = -1;
struct varnode_t {
char name[VARNAME_MAX + 1];
@@ -64,19 +64,19 @@ struct varnode_t {
struct varnode_t *next;
};
-void error(const char *fmt, ...) __attribute__((noreturn,format(print,1,2)));
-void vid_write(const char *str);
-void vid_writef(const char *fmt, ...) __attribute__((format(printf,1,2)));
-void debug(const char *fmt, ...) __attribute__((format(printf,1,2)));
-bool isValidVariable(const char *c);
+static void error(const char *fmt, ...) __attribute__((noreturn,format(print,1,2)));
+static void vid_write(const char *str);
+static void vid_writef(const char *fmt, ...) __attribute__((format(printf,1,2)));
+static void debug(const char *fmt, ...) __attribute__((format(printf,1,2)));
+static bool isValidVariable(const char *c);
/* variables are stored in a chained hash map */
/* collisions are manageable, but should be minimized */
-struct varnode_t *var_map[VARMAP_SIZE];
+static struct varnode_t *var_map[VARMAP_SIZE];
/* simple DJB hash */
-uint32_t var_hash(const char *str)
+static uint32_t var_hash(const char *str)
{
uint32_t hash = 5381;
char c;
@@ -88,7 +88,7 @@ uint32_t var_hash(const char *str)
return hash;
}
-struct varnode_t *lookup_var(const char *name)
+static struct varnode_t *lookup_var(const char *name)
{
uint32_t hash = var_hash(name) % VARMAP_SIZE;
@@ -120,12 +120,12 @@ struct varnode_t *lookup_var(const char *name)
return new;
}
-vartype getVariable(const char *name)
+static vartype getVariable(const char *name)
{
return lookup_var(name)->val;
}
-void setVariable(const char *name, vartype val)
+static void setVariable(const char *name, vartype val)
{
struct varnode_t *node = lookup_var(name);
if(!node->constant)
@@ -134,12 +134,12 @@ void setVariable(const char *name, vartype val)
error("attempted to modify a constant variable");
}
-void setConst(const char *name, bool c)
+static void setConst(const char *name, bool c)
{
lookup_var(name)->constant = c;
}
-void incVar(const char *name)
+static void incVar(const char *name)
{
struct varnode_t *node = lookup_var(name);
if(!node->constant)
@@ -148,7 +148,7 @@ void incVar(const char *name)
error("attempted to modify a constant variable");
}
-void decVar(const char *name)
+static void decVar(const char *name)
{
struct varnode_t *node = lookup_var(name);
if(!node->constant)
@@ -159,12 +159,10 @@ void decVar(const char *name)
/*** Utility functions ***/
-void exit_handler(void)
+static void exit_handler(void)
{
if(file_des >= 0)
close(file_des);
- if(log_fd >= 0)
- close(log_fd);
if(line_offset)
free(line_offset);
/* free all our variables */
@@ -180,12 +178,12 @@ void exit_handler(void)
}
}
-void vid_write(const char *str)
+static void vid_write(const char *str)
{
printf("%s", str);
}
-void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...)
+static void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...)
{
char fmtbuf[256];
@@ -196,7 +194,7 @@ void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...)
va_end(ap);
}
-void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...)
+static void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...)
{
char fmtbuf[256];
@@ -211,7 +209,7 @@ void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...)
exit(EXIT_FAILURE);
}
-void __attribute__((format(printf,1,2))) warning(const char *fmt, ...)
+static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...)
{
char fmtbuf[256];
@@ -223,7 +221,7 @@ void __attribute__((format(printf,1,2))) warning(const char *fmt, ...)
}
/* grabs a line from a file, -1 on error, returns # bytes read otherwise */
-int read_line(int fd, char *buf, size_t sz)
+static int read_line(int fd, char *buf, size_t sz)
{
unsigned i = 0;
int bytes_read = 0;
@@ -254,7 +252,7 @@ int read_line(int fd, char *buf, size_t sz)
/* index_lines() precalculates the offset of each line for faster jumping */
/* also it does a quick pass to index all the labels */
-off_t *index_lines(int fd, unsigned *numlines)
+static off_t *index_lines(int fd, unsigned *numlines)
{
size_t sz = sizeof(off_t);
off_t *data = malloc(sz);
@@ -295,7 +293,7 @@ off_t *index_lines(int fd, unsigned *numlines)
return data;
}
-void jump_line(int fd, unsigned where)
+static void jump_line(int fd, unsigned where)
{
if(1 <= where && where <= num_lines)
{
@@ -306,7 +304,7 @@ void jump_line(int fd, unsigned where)
current_line = where - 1;
}
-void sub_call(int fd, unsigned where)
+static void sub_call(int fd, unsigned where)
{
if(stack_frame < ARRAYLEN(call_stack))
{
@@ -318,7 +316,7 @@ void sub_call(int fd, unsigned where)
error("call stack overflow");
}
-void sub_return(int fd)
+static void sub_return(int fd)
{
if(stack_frame > 0)
{
@@ -331,104 +329,104 @@ void sub_return(int fd)
/* based on http://en.literateprograms.org/Shunting_yard_algorithm_%28C%29 */
-vartype eval_uminus(vartype a1, vartype a2)
+static vartype eval_uminus(vartype a1, vartype a2)
{
(void) a2;
return -a1;
}
-vartype eval_exp(vartype a1, vartype a2)
+static vartype eval_exp(vartype a1, vartype a2)
{
return a2<0 ? 0 : (a2==0?1:a1*eval_exp(a1, a2-1));
}
-vartype eval_mul(vartype a1, vartype a2)
+static vartype eval_mul(vartype a1, vartype a2)
{
return a1*a2;
}
-vartype eval_div(vartype a1, vartype a2)
+static vartype eval_div(vartype a1, vartype a2)
{
if(!a2) {
error("division by zero");
}
return a1/a2;
}
-vartype eval_mod(vartype a1, vartype a2)
+static vartype eval_mod(vartype a1, vartype a2)
{
if(!a2) {
error("division by zero");
}
return a1%a2;
}
-vartype eval_add(vartype a1, vartype a2)
+static vartype eval_add(vartype a1, vartype a2)
{
return a1+a2;
}
-vartype eval_sub(vartype a1, vartype a2)
+static vartype eval_sub(vartype a1, vartype a2)
{
return a1-a2;
}
-vartype eval_eq(vartype a1, vartype a2)
+static vartype eval_eq(vartype a1, vartype a2)
{
return a1 == a2;
}
-vartype eval_neq(vartype a1, vartype a2)
+static vartype eval_neq(vartype a1, vartype a2)
{
return a1 != a2;
}
-vartype eval_leq(vartype a1, vartype a2)
+static vartype eval_leq(vartype a1, vartype a2)
{
return a1 <= a2;
}
-vartype eval_geq(vartype a1, vartype a2)
+static vartype eval_geq(vartype a1, vartype a2)
{
return a1 >= a2;
}
-vartype eval_lt(vartype a1, vartype a2)
+static vartype eval_lt(vartype a1, vartype a2)
{
return a1 < a2;
}
-vartype eval_gt(vartype a1, vartype a2)
+static vartype eval_gt(vartype a1, vartype a2)
{
return a1 > a2;
}
-vartype eval_log_neg(vartype a1, vartype a2)
+static vartype eval_log_neg(vartype a1, vartype a2)
{
(void) a2;
return !a1;
}
-vartype eval_log_and(vartype a1, vartype a2)
+static vartype eval_log_and(vartype a1, vartype a2)
{
return a1 && a2;
}
-vartype eval_log_or(vartype a1, vartype a2)
+static vartype eval_log_or(vartype a1, vartype a2)
{
return a1 || a2;
}
-vartype eval_bit_and(vartype a1, vartype a2)
+static vartype eval_bit_and(vartype a1, vartype a2)
{
return a1 & a2;
}
-vartype eval_bit_xor(vartype a1, vartype a2)
+static vartype eval_bit_xor(vartype a1, vartype a2)
{
return a1 ^ a2;
}
-vartype eval_bit_or(vartype a1, vartype a2)
+static vartype eval_bit_or(vartype a1, vartype a2)
{
return a1 | a2;
}
-vartype eval_bit_comp(vartype a1, vartype a2)
+static vartype eval_bit_comp(vartype a1, vartype a2)
{
(void) a2;
return ~a1;
}
-vartype eval_lsh(vartype a1, vartype a2)
+static vartype eval_lsh(vartype a1, vartype a2)
{
return a1 << a2;
}
-vartype eval_rsh(vartype a1, vartype a2)
+static vartype eval_rsh(vartype a1, vartype a2)
{
return a1 >> a2;
}
-vartype eval_sqrt(vartype a1, vartype a2)
+static vartype eval_sqrt(vartype a1, vartype a2)
{
(void) a2;
return sqrt(a1);
@@ -462,7 +460,7 @@ enum {ASSOC_NONE=0, ASSOC_LEFT, ASSOC_RIGHT};
/* arrays are implemented as UNARY OPERATORS */
-struct op_s {
+static struct op_s {
const char *op;
int prec;
int assoc;
@@ -498,13 +496,13 @@ struct op_s {
#define OPMAP_SIZE 25
-void op_hash_round(char c, uint32_t *hash)
+static void op_hash_round(char c, uint32_t *hash)
{
*hash *= 70;
*hash ^= c;
}
-uint32_t op_hash(const char *c)
+static uint32_t op_hash(const char *c)
{
uint32_t hash = 4412;
while(1)
@@ -517,10 +515,10 @@ uint32_t op_hash(const char *c)
}
/* optimized hash map for fast lookup of operators */
-struct op_s op_map[OPMAP_SIZE];
-size_t longest_op = 0;
+static struct op_s op_map[OPMAP_SIZE];
+static size_t longest_op = 0;
-void opmap_insert(struct op_s *op)
+static void opmap_insert(struct op_s *op)
{
if(op->len > longest_op)
longest_op = op->len;
@@ -532,7 +530,7 @@ void opmap_insert(struct op_s *op)
memcpy(op_map+hash, op, sizeof(*op));
}
-void init_optable(void)
+static void init_optable(void)
{
memset(op_map, 0, sizeof(op_map));
for(unsigned int i = 0; i < ARRAYLEN(ops); ++i)
@@ -542,7 +540,7 @@ void init_optable(void)
}
}
-const struct op_s *getop(const char *ch, int *len)
+static const struct op_s *getop(const char *ch, int *len)
{
unsigned int i = 0;
uint32_t hash = 4412;
@@ -560,13 +558,13 @@ const struct op_s *getop(const char *ch, int *len)
return poss;
}
-const struct op_s *opstack[MAXOPSTACK];
-int nopstack;
+static const struct op_s *opstack[MAXOPSTACK];
+static int nopstack;
-vartype numstack[MAXNUMSTACK];
-int nnumstack;
+static vartype numstack[MAXNUMSTACK];
+static int nnumstack;
-void push_opstack(const struct op_s *op)
+static void push_opstack(const struct op_s *op)
{
if(nopstack>MAXOPSTACK - 1) {
error("operator stack overflow");
@@ -574,7 +572,7 @@ void push_opstack(const struct op_s *op)
opstack[nopstack++] = op;
}
-const struct op_s *pop_opstack(void)
+static const struct op_s *pop_opstack(void)
{
if(!nopstack) {
error("operator stack empty");
@@ -582,7 +580,7 @@ const struct op_s *pop_opstack(void)
return opstack[--nopstack];
}
-void push_numstack(vartype num)
+static void push_numstack(vartype num)
{
if(nnumstack>MAXNUMSTACK - 1) {
error("number stack overflow");
@@ -590,7 +588,7 @@ void push_numstack(vartype num)
numstack[nnumstack++] = num;
}
-vartype pop_numstack(void)
+static vartype pop_numstack(void)
{
if(!nnumstack) {
error("number stack empty");
@@ -598,12 +596,12 @@ vartype pop_numstack(void)
return numstack[--nnumstack];
}
-bool isDigit(char c)
+static bool isDigit(char c)
{
return '0' <= c && c <= '9';
}
-bool isValidNumber(char *str)
+static bool isValidNumber(char *str)
{
//vid_writef("isValidNumber %s", str);
if(str && (isDigit(*str) || *str == '-'))
@@ -621,13 +619,13 @@ bool isValidNumber(char *str)
return false;
}
-bool isSpace(char c)
+static bool isSpace(char c)
{
//vid_writef("isSpace '%c'", c);
return (c == ' ') || (c == '\t');
}
-bool isValidVariable(const char *c)
+static bool isValidVariable(const char *c)
{
//vid_writef("isValidVariable %s", c);
if(!isDigit(*c) && !getop(c, NULL) && !isSpace(*c))
@@ -637,7 +635,7 @@ bool isValidVariable(const char *c)
return false;
}
-vartype getValue(char *str, char *cur)
+static vartype getValue(char *str, char *cur)
{
//vid_writef("getValue %s", str);
if(str && isValidVariable(str))
@@ -650,7 +648,7 @@ vartype getValue(char *str, char *cur)
return strtol(str, NULL, 0);
}
-bool isValidNumberOrVariable(const char *c)
+static bool isValidNumberOrVariable(const char *c)
{
//vid_writef("isValidNumberOrVariable %s", c);
if(isDigit(*c) || isValidVariable(c))
@@ -660,7 +658,7 @@ bool isValidNumberOrVariable(const char *c)
return false;
}
-void shunt_op(const struct op_s *op)
+static void shunt_op(const struct op_s *op)
{
const struct op_s *pop;
vartype n1, n2;
@@ -726,7 +724,7 @@ void shunt_op(const struct op_s *op)
push_opstack(op);
}
-vartype eval_expr(char *str)
+static vartype eval_expr(char *str)
{
//vid_write("**************** EVAL EXPR ***************");
@@ -825,7 +823,7 @@ vartype eval_expr(char *str)
#define NEXT 2
#define BREAK 3
-int let_handler(char **save, int *repeats_left)
+static int let_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *varname = strtok_r(NULL, "= \t", save);
@@ -849,7 +847,7 @@ int let_handler(char **save, int *repeats_left)
return OK;
}
-int repeat_handler(char **save, int *repeats_left)
+static int repeat_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, ";", save);
@@ -865,7 +863,7 @@ int repeat_handler(char **save, int *repeats_left)
error("expected valid expression after REPEAT");
}
-int goto_handler(char **save, int *repeats_left)
+static int goto_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, ";", save);
@@ -878,7 +876,7 @@ int goto_handler(char **save, int *repeats_left)
error("expected valid expression after GOTO");
}
-int call_handler(char **save, int *repeats_left)
+static int call_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, "", save);
@@ -891,14 +889,14 @@ int call_handler(char **save, int *repeats_left)
error("expected destination for CALL");
}
-int ret_handler(char **save, int *repeats_left)
+static int ret_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
sub_return(file_des);
return NEXT;
}
-int inc_handler(char **save, int *repeats_left)
+static int inc_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, " \t", save);
@@ -907,7 +905,7 @@ int inc_handler(char **save, int *repeats_left)
return OK;
}
-int dec_handler(char **save, int *repeats_left)
+static int dec_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, " \t", save);
@@ -916,7 +914,7 @@ int dec_handler(char **save, int *repeats_left)
return OK;
}
-int if_handler(char **save, int *repeats_left)
+static int if_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, ";", save);
@@ -932,7 +930,7 @@ int if_handler(char **save, int *repeats_left)
return OK;
}
-int delay_handler(char **save, int *repeats_left)
+static int delay_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
/* delay N 1000ths of a sec */
@@ -951,7 +949,7 @@ int delay_handler(char **save, int *repeats_left)
return OK;
}
-int log_handler(char **save, int *repeats_left)
+static int log_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, "", save);
@@ -960,7 +958,7 @@ int log_handler(char **save, int *repeats_left)
return OK;
}
-int logvar_handler(char **save, int *repeats_left)
+static int logvar_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
char *tok = strtok_r(NULL, ";", save);
@@ -973,26 +971,26 @@ int logvar_handler(char **save, int *repeats_left)
error("expected expression after LOGVAR");
}
-int rem_handler(char **save, int *repeats_left)
+static int rem_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
return BREAK;
}
-int quit_handler(char **save, int *repeats_left)
+static int quit_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
return DONE;
}
-int newline_handler(char **save, int *repeats_left)
+static int newline_handler(char **save, int *repeats_left)
{
(void) save; (void) repeats_left;
vid_write("\n");
return OK;
}
-int logchar_handler(char **save, int *repeats_left)
+static int logchar_handler(char **save, int *repeats_left)
{
(void) repeats_left;
char *tok = strtok_r(NULL, ";", save);
@@ -1006,7 +1004,7 @@ int logchar_handler(char **save, int *repeats_left)
}
-struct token_t {
+static struct token_t {
const char *tok;
int (*func)(char **save, int *repeats_left);
} tokens[] = {
@@ -1037,7 +1035,7 @@ struct token_t {
/* once again, this lookup table is implemented with a perfect hash map */
#define TOKMAP_SIZE ARRAYLEN(tokens)
-struct token_t tokmap[TOKMAP_SIZE];
+static struct token_t tokmap[TOKMAP_SIZE];
/* auto-generated with mph-1.2 */
/*
@@ -1165,7 +1163,7 @@ static int T2[] = {
#define uchar unsigned char
-int
+static int
tok_hash(const uchar *key)
{
int i;
@@ -1193,7 +1191,7 @@ tok_hash(const uchar *key)
return (g[f0] + g[f1] + g[f2]) % 22;
}
-void tokmap_insert(struct token_t *tok)
+static void tokmap_insert(struct token_t *tok)
{
uint32_t hash = tok_hash(tok->tok) % TOKMAP_SIZE;
if(hash < 0 || tokmap[hash].tok)
@@ -1201,7 +1199,7 @@ void tokmap_insert(struct token_t *tok)
memcpy(tokmap+hash, tok, sizeof(*tok));
}
-void init_tokmap(void)
+static void init_tokmap(void)
{
memset(tokmap, 0, sizeof(tokmap));
for(unsigned int i = 0; i < ARRAYLEN(tokens); ++i)
@@ -1210,10 +1208,9 @@ void init_tokmap(void)
}
}
-void init_globals(void)
+static void init_globals(void)
{
line_offset = NULL;
- log_fd = -1;
file_des = -1;
stack_frame = 0;
lines_executed = 0;
diff --git a/src/opcodes.h b/src/opcodes.h
new file mode 100644
index 0000000..a4ca890
--- /dev/null
+++ b/src/opcodes.h
@@ -0,0 +1,39 @@
+#define PUSHIMM 0x00
+#define PUSHVAR 0x01
+#define POP 0x02
+#define MKCONST 0x03
+#define INCVAR 0x04
+#define DECVAR 0x05
+#define WRITE_STR 0x06
+#define REPEAT 0x07
+#define JUMP 0x08
+#define SUBCALL 0x09
+#define SUBRET 0x0A
+#define IF 0x0B
+#define DELAY 0x0C
+#define LOGVAR 0x0D
+#define QUIT 0x0E
+#define LOGASCII 0x0F
+#define NEG 0x10
+#define POW 0x11
+#define MULT 0x12
+#define DIV 0x13
+#define MOD 0x14
+#define ADD 0x15
+#define SUB 0x16
+#define EQ 0x17
+#define NEQ 0x18
+#define LEQ 0x19
+#define GEQ 0x1A
+#define LT 0x1B
+#define GT 0x1C
+#define LOGNOT 0x1D
+#define LOGAND 0x1E
+#define LOGOR 0x1F
+#define BITAND 0x20
+#define BITOR 0x21
+#define BITXOR 0x22
+#define BITCOMP 0x23
+#define LSH 0x24
+#define RSH 0x25
+#define SQRT 0x26