diff options
| author | Franklin Wei <git@fwei.tk> | 2015-11-08 12:45:58 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2015-11-08 12:45:58 -0500 |
| commit | 768628ea1767b966418f21dced0572e222a6208b (patch) | |
| tree | 74395ea195bddb3ec876febd52d025e06ec410e4 /src/ducky.c | |
| parent | 89e35bddfb92c6c37177d9830bf0664e9abb29d9 (diff) | |
| download | ducky-768628ea1767b966418f21dced0572e222a6208b.zip ducky-768628ea1767b966418f21dced0572e222a6208b.tar.gz ducky-768628ea1767b966418f21dced0572e222a6208b.tar.bz2 ducky-768628ea1767b966418f21dced0572e222a6208b.tar.xz | |
rename to interpreter
Diffstat (limited to 'src/ducky.c')
| -rw-r--r-- | src/ducky.c | 1336 |
1 files changed, 0 insertions, 1336 deletions
diff --git a/src/ducky.c b/src/ducky.c deleted file mode 100644 index e3b00b6..0000000 --- a/src/ducky.c +++ /dev/null @@ -1,1336 +0,0 @@ -#include <platform.h> - -/******************************************************************************* - * The scripting language implemented here is an extension of DuckyScript. - * DuckyScript as it is now is limited to simple tasks, as it lacks good flow - * control or variable support. - * - * These following extensions to DuckyScript are (to be) implemented. - * - * Variables: variable names consist of all characters that are not operators - * or digits. The limit on variable names is 24 characters. - * - * NOTE: to have a command on a line following a command using an expression, a - * semicolon (;) is needed to separate them. - * - * "JUMP <EXPR>;" - jumps to line <EXPR> in the file - * - * "IF <CONDITION>;" - if <CONDITION> equals zero, skip the rest of the line - * - * "LET X=<EXPR>;" - loads the value of <EXPR> into variable X. Greedy. - * - * "LOG ..." - outputs any remaining text to the device's screen. - * "LOGVAR <EXPR>;" - outputs variable <EXPR> in decimal to the device's screen - * "NEWLINE" - outputs a newline - * "LOGCHAR <EXPR>;" - outputs <EXPR> as an ASCII character - ******************************************************************************/ - -/*** 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 ***/ - -off_t *line_offset = NULL; - -unsigned lines_executed = 0, current_line = 0, num_lines; - -unsigned call_stack[CALL_STACK_SZ]; -unsigned stack_frame = 0; - -int log_fd = -1, file_des = -1; - -struct varnode_t { - char name[VARNAME_MAX + 1]; - vartype val; - bool constant; /* used by setVariable */ - 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); - -/* variables are stored in a chained hash map */ -/* collisions are manageable, but should be minimized */ - -struct varnode_t *var_map[VARMAP_SIZE]; - -/* simple DJB hash */ -uint32_t var_hash(const char *str) -{ - uint32_t hash = 5381; - char c; - while((c = *str++)) - { - hash = ((hash << 5) + hash) ^ c; - } - - return hash; -} - -struct varnode_t *lookup_var(const char *name) -{ - uint32_t hash = var_hash(name) % VARMAP_SIZE; - - struct varnode_t *iter = var_map[hash]; - struct varnode_t *last = NULL; - - while(iter) - { - if(strcmp(iter->name, name) == 0) - return iter; - last = iter; - iter = iter->next; - } - - /* not found in this bucket, so add it to the linked list */ - struct varnode_t *new = malloc(sizeof(struct varnode_t)); - - memset(new, 0, sizeof(struct varnode_t)); - strlcpy(new->name, name, sizeof(new->name)); - new->val = 0; - new->constant = false; - new->next = NULL; - - if(!last) - var_map[hash] = new; - else - last->next = new; - - return new; -} - -vartype getVariable(const char *name) -{ - return lookup_var(name)->val; -} - -void setVariable(const char *name, vartype val) -{ - struct varnode_t *node = lookup_var(name); - if(!node->constant) - node->val = val; - else - error("attempted to modify a constant variable"); -} - -void setConst(const char *name, bool c) -{ - lookup_var(name)->constant = c; -} - -void incVar(const char *name) -{ - struct varnode_t *node = lookup_var(name); - if(!node->constant) - ++lookup_var(name)->val; - else - error("attempted to modify a constant variable"); -} - -void decVar(const char *name) -{ - struct varnode_t *node = lookup_var(name); - if(!node->constant) - --lookup_var(name)->val; - else - error("attempted to modify a constant variable"); -} - -/*** Utility functions ***/ - -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 */ - for(int i = 0; i < VARMAP_SIZE; ++i) - { - struct varnode_t *iter = var_map[i], *next; - while(iter) - { - next = iter->next; - free(iter); - iter = next; - } - } -} - -void vid_write(const char *str) -{ - printf("%s", str); -} - -void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...) -{ - char fmtbuf[256]; - - va_list ap; - va_start(ap, fmt); - vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap); - vid_write(fmtbuf); - va_end(ap); -} - -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_writef("Line %d: ", current_line); - vid_writef("ERROR: %s\n", fmtbuf); - va_end(ap); - - exit(EXIT_FAILURE); -} - -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_writef("Line %d: WARNING: %s\n", current_line, fmtbuf); - va_end(ap); -} - -/* grabs a line from a file, -1 on error, returns # bytes read otherwise */ -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 */ - -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); - lookup_var(tok)->constant = true; - } - } - - ++idx; - } - - lseek(fd, 0, SEEK_SET); - - *numlines = idx - 1; - - return data; -} - -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; -} - -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"); -} - -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 */ - -vartype eval_uminus(vartype a1, vartype a2) -{ - (void) a2; - return -a1; -} -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) -{ - return a1*a2; -} -vartype eval_div(vartype a1, vartype a2) -{ - if(!a2) { - error("division by zero"); - } - return a1/a2; -} -vartype eval_mod(vartype a1, vartype a2) -{ - if(!a2) { - error("division by zero"); - } - return a1%a2; -} -vartype eval_add(vartype a1, vartype a2) -{ - return a1+a2; -} -vartype eval_sub(vartype a1, vartype a2) -{ - return a1-a2; -} -vartype eval_eq(vartype a1, vartype a2) -{ - return a1 == a2; -} -vartype eval_neq(vartype a1, vartype a2) -{ - return a1 != a2; -} -vartype eval_leq(vartype a1, vartype a2) -{ - return a1 <= a2; -} -vartype eval_geq(vartype a1, vartype a2) -{ - return a1 >= a2; -} -vartype eval_lt(vartype a1, vartype a2) -{ - return a1 < a2; -} -vartype eval_gt(vartype a1, vartype a2) -{ - return a1 > a2; -} -vartype eval_log_neg(vartype a1, vartype a2) -{ - (void) a2; - return !a1; -} -vartype eval_log_and(vartype a1, vartype a2) -{ - return a1 && a2; -} -vartype eval_log_or(vartype a1, vartype a2) -{ - return a1 || a2; -} -vartype eval_bit_and(vartype a1, vartype a2) -{ - return a1 & a2; -} -vartype eval_bit_xor(vartype a1, vartype a2) -{ - return a1 ^ a2; -} -vartype eval_bit_or(vartype a1, vartype a2) -{ - return a1 | a2; -} -vartype eval_bit_comp(vartype a1, vartype a2) -{ - (void) a2; - return ~a1; -} -vartype eval_lsh(vartype a1, vartype a2) -{ - return a1 << a2; -} -vartype eval_rsh(vartype a1, vartype a2) -{ - return a1 >> a2; -} -vartype eval_sqrt(vartype a1, vartype a2) -{ - (void) a2; - return sqrt(a1); -} - -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 */ - -struct op_s { - const char *op; - int prec; - int assoc; - int unary; - vartype (*eval)(vartype a1, vartype a2); - 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 - -void op_hash_round(char c, uint32_t *hash) -{ - *hash *= 70; - *hash ^= c; -} - -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 */ -struct op_s op_map[OPMAP_SIZE]; -size_t longest_op = 0; - -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)); -} - -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); - } -} - -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; -} - -const struct op_s *opstack[MAXOPSTACK]; -int nopstack; - -vartype numstack[MAXNUMSTACK]; -int nnumstack; - -void push_opstack(const struct op_s *op) -{ - if(nopstack>MAXOPSTACK - 1) { - error("operator stack overflow"); - } - opstack[nopstack++] = op; -} - -const struct op_s *pop_opstack(void) -{ - if(!nopstack) { - error("operator stack empty"); - } - return opstack[--nopstack]; -} - -void push_numstack(vartype num) -{ - if(nnumstack>MAXNUMSTACK - 1) { - error("number stack overflow"); - } - numstack[nnumstack++] = num; -} - -vartype pop_numstack(void) -{ - if(!nnumstack) { - error("number stack empty"); - } - return numstack[--nnumstack]; -} - -bool isDigit(char c) -{ - return '0' <= c && c <= '9'; -} - -bool isValidNumber(char *str) -{ - //vid_writef("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; -} - -bool isSpace(char c) -{ - //vid_writef("isSpace '%c'", c); - return (c == ' ') || (c == '\t'); -} - -bool isValidVariable(const char *c) -{ - //vid_writef("isValidVariable %s", c); - if(!isDigit(*c) && !getop(c, NULL) && !isSpace(*c)) - { - return true; - } - return false; -} - -vartype getValue(char *str, char *cur) -{ - //vid_writef("getValue %s", str); - if(str && isValidVariable(str)) - { - /* isolate the variable name into a buffer */ - char varname[VARNAME_MAX + 1] = { 0 }; - memcpy(varname, str, cur - str); - return getVariable(varname); - } - return strtol(str, NULL, 0); -} - -bool isValidNumberOrVariable(const char *c) -{ - //vid_writef("isValidNumberOrVariable %s", c); - if(isDigit(*c) || isValidVariable(c)) - { - return true; - } - return false; -} - -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(); - n1 = pop_numstack(); - - if(pop->unary) - push_numstack(pop->eval(n1, 0)); - else - { - n2 = pop_numstack(); - push_numstack(pop->eval(n2, n1)); - } - } - - 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(); - n1 = pop_numstack(); - if(pop->unary) - push_numstack(pop->eval(n1, 0)); - else - { - n2 = pop_numstack(); - push_numstack(pop->eval(n2, n1)); - } - } - } - else - { - while(nopstack && op->prec<opstack[nopstack - 1]->prec) - { - pop = pop_opstack(); - n1 = pop_numstack(); - if(pop->unary) - push_numstack(pop->eval(n1, 0)); - else - { - n2 = pop_numstack(); - push_numstack(pop->eval(n2, n1)); - } - } - } - - push_opstack(op); -} - -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; - - nopstack = 0; - nnumstack = 0; - - 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)); - tstart = NULL; - lastop = NULL; - } - else if((op = getop(expr, &len))) - { - push_numstack(getValue(tstart, expr)); - tstart = NULL; - shunt_op(op); - lastop = op; - } - else if(!isValidNumberOrVariable(expr)) - { - error("syntax error"); - } - } - } - - if(tstart) - push_numstack(getValue(tstart, expr)); - - while(nopstack) { - op = pop_opstack(); - n1 = pop_numstack(); - if(!op->unary) - { - n2 = pop_numstack(); - push_numstack(op->eval(n2, n1)); - } - else - push_numstack(op->eval(n1, 0)); - } - - if(nnumstack != 1) { - error("invalid expression"); - } - - return numstack[0]; -} - -#define OK 0 -#define DONE 1 -#define NEXT 2 -#define BREAK 3 - -int let_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *varname = strtok_r(NULL, "= \t", save); - - if(varname && isValidVariable(varname)) - { - struct varnode_t *node = lookup_var(varname); - if(node->constant) - error("attempted to modify a constant variable"); - - char *tok = strtok_r(NULL, "=;", save); - if(tok) - node->val = eval_expr(tok); - else - error("exprected valid expression after LET"); - } - else - { - error("invalid variable name for LET"); - } - return OK; -} - -int repeat_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, ";", save); - if(tok) - { - if(current_line == 1) - error("REPEAT without a previous instruction"); - *repeats_left = eval_expr(tok) - 1; - jump_line(file_des, current_line - 1); - return NEXT; - } - else - error("expected valid expression after REPEAT"); -} - -int goto_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, ";", save); - if(tok) - { - jump_line(file_des, eval_expr(tok)); - return NEXT; - } - else - error("expected valid expression after GOTO"); -} - -int call_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, "", save); - if(tok) - { - sub_call(file_des, eval_expr(tok)); - return NEXT; - } - else - error("expected destination for CALL"); -} - -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) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, " \t", save); - if(isValidVariable(tok)) - incVar(tok); - return OK; -} - -int dec_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, " \t", save); - if(isValidVariable(tok)) - decVar(tok); - return OK; -} - -int if_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, ";", save); - - if(!tok) - error("expected conditional after IF"); - - /* break out of the do-while if the condition is false */ - if(!eval_expr(tok)) - { - return BREAK; - } - return OK; -} - -int delay_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - /* delay N 1000ths of a sec */ - char *tok = strtok_r(NULL, ";", save); - if(tok) - { - int ms = eval_expr(tok); - struct timespec t; - t.tv_sec = ms / 1000; - t.tv_nsec = (ms % 1000) * 1000000; - nanosleep(&t, NULL); - } - else - error("expected valid expression after DELAY"); - - return OK; -} - -int log_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, "", save); - - vid_write(tok); - return OK; -} - -int logvar_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - char *tok = strtok_r(NULL, ";", save); - if(tok) - { - vid_writef(VARFORMAT, eval_expr(tok)); - return OK; - } - else - error("expected expression after LOGVAR"); -} - -int rem_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - return BREAK; -} - -int quit_handler(char **save, int *repeats_left) -{ - (void) save; (void) repeats_left; - return DONE; -} - -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) -{ - (void) repeats_left; - char *tok = strtok_r(NULL, ";", save); - if(tok) - { - vid_writef("%c", eval_expr(tok)); - return OK; - } - else - error("expected expression after LOGCHAR"); - -} - -struct token_t { - const char *tok; - int (*func)(char **save, int *repeats_left); -} 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) -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 - -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; -} - -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)); -} - -void init_tokmap(void) -{ - memset(tokmap, 0, sizeof(tokmap)); - for(unsigned int i = 0; i < ARRAYLEN(tokens); ++i) - { - tokmap_insert(tokens+i); - } -} - -void init_globals(void) -{ - line_offset = NULL; - log_fd = -1; - file_des = -1; - stack_frame = 0; - lines_executed = 0; - current_line = 0; - memset(var_map, 0, sizeof(var_map)); -} - -void ducky_main(int fd, bool verbose) -{ - init_globals(); - - if(verbose) - { - vid_write("*** DS-2 INIT ***"); - vid_write("QUACK AT YOUR OWN RISK!"); - vid_write("The author assumes no liability for any damages caused by this program."); - } - - file_des = fd; - - atexit(exit_handler); - - if(file_des < 0) - error("invalid file"); - - init_optable(); - init_tokmap(); - - /* initialize the "." variable, which is the line counter */ - setVariable(".", 0); - setConst(".", true); - - struct varnode_t *dot_var = lookup_var("."); - - /* 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_writef("Indexing complete (%u lines).", num_lines); - - vid_write("Executing..."); - } - 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_writef("end of file"); - goto done; - } - char *tok = NULL, *save = NULL; - - ++current_line; - dot_var->val = current_line; - ++lines_executed; - - 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, &repeats_left)) - { - 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: - - if(repeats_left > 0) - { - --repeats_left; - if(repeats_left) - jump_line(file_des, current_line); - else - { - if(current_line + 2 > num_lines) - goto done; - jump_line(file_des, current_line + 2); - } - } - next_line: - ; - } - -done: - - return; -} |