diff options
| author | Franklin Wei <git@fwei.tk> | 2015-11-25 22:36:40 -0500 |
|---|---|---|
| committer | Franklin Wei <git@fwei.tk> | 2015-11-25 22:36:40 -0500 |
| commit | f9bd70a047c357d7f4ebfadbfe34c30becb380db (patch) | |
| tree | 7e991c719d0f40a453f9e8a83ccb9760ba6f3a4c | |
| parent | 3c13f12ac8279ddccf2a20fbc072d6b918ea2096 (diff) | |
| download | ducky-f9bd70a047c357d7f4ebfadbfe34c30becb380db.zip ducky-f9bd70a047c357d7f4ebfadbfe34c30becb380db.tar.gz ducky-f9bd70a047c357d7f4ebfadbfe34c30becb380db.tar.bz2 ducky-f9bd70a047c357d7f4ebfadbfe34c30becb380db.tar.xz | |
refactor, add simple builtin functions, fix osx bug
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/common.c | 142 | ||||
| -rw-r--r-- | src/compile.c | 233 | ||||
| -rw-r--r-- | src/ducky.h | 32 | ||||
| -rw-r--r-- | src/emitc.c | 96 | ||||
| -rw-r--r-- | src/interp.c | 210 | ||||
| -rw-r--r-- | src/opcodes.h | 1 | ||||
| -rw-r--r-- | src/vm.c | 77 |
8 files changed, 416 insertions, 377 deletions
@@ -2,7 +2,7 @@ CC = gcc OUT = build PLATFORM = unix -DUCKY_OBJ = src/interp.o src/compile.o src/vm.o src/emitc.o +DUCKY_OBJ = src/interp.o src/compile.o src/vm.o src/emitc.o src/common.o CFLAGS = -lm -Og -g -I src/ -I target/$(PLATFORM) diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..b25b6a2 --- /dev/null +++ b/src/common.c @@ -0,0 +1,142 @@ +#include "ducky.h" + +vartype spec_zero(void) +{ + return 0; +} + +vartype spec_rand(void) +{ + return rand(); +} + +vartype spec_time(void) +{ + return time(NULL); +} + +vartype (*special_funcs[])(void) = { + spec_zero, + spec_rand, + spec_time, +}; + +void __attribute__((noreturn,format(printf,3,4))) error(jmp_buf exit_point, int current_line, 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", fmtbuf); + va_end(ap); + + longjmp(exit_point, 1); +} + +vartype get_special(enum special_id special, jmp_buf exit_point, int current_line) +{ + if(special < ARRAYLEN(special_funcs)) + return special_funcs[special](); + else + error(exit_point, current_line, "unknown special stream"); +} + +/* 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 */ + +off_t *index_lines(int fd, unsigned *numlines, bool labels, bool (*isValidVariable)(const char*), void (*setConst)(const char*, bool), void (*setVariable)(const char*, vartype)) +{ + 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; + + if(labels) + { + 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; +} + +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); +} + +void __attribute__((format(printf,2,3))) warning_real(int current_line, 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); +} diff --git a/src/compile.c b/src/compile.c index 20de9b1..2b53695 100644 --- a/src/compile.c +++ b/src/compile.c @@ -42,9 +42,7 @@ struct var_t { 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); @@ -119,6 +117,13 @@ static void setConst(const char *name, bool c) } } +static void setSpecial(const char *name, enum special_id spec) +{ + write_instr(MKSPECIAL); + write_varid(get_varid(name)); + write_imm(spec); +} + static void incVar(const char *name) { write_instr(INCVAR); @@ -141,7 +146,7 @@ static void exit_handler(void) free(line_offset); } -static void vid_write(const char *str) +static void write_str(const char *str) { write_instr(WRITE_STR); while(*str) @@ -151,134 +156,6 @@ static void vid_write(const char *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", fmtbuf); - va_end(ap); - - longjmp(exit_point, 1); -} - -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; -} - -/* depends on index_lines, indexes labels in the file */ -static void index_labels(int fd) -{ - for(unsigned i = 1; i <= num_lines; ++i) - { - lseek(fd, line_offset[i], SEEK_SET); - char buf[MAX_LINE_LEN]; - int status = read_line(fd, buf, sizeof(buf)); - - /* exit early if failed or too short for a label */ - if(status < strlen("LBL")) - 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); - - vid_logf("found label %s val %d", tok, i); - if(tok && isValidVariable(tok)) - { - setVariable(tok, i); - setConst(tok, true); - } - } - } - lseek(fd, 0, SEEK_SET); -} - -/* index_lines() precalculates the offset of each line for faster jumping */ -/* the line data is only used by index_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; - - ++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) @@ -286,7 +163,7 @@ static void jump_line(int fd, unsigned where) lseek(fd, line_offset[where], SEEK_SET); } else - error("JUMP target out of range (%u)", where); + ERROR("JUMP target out of range (%u)", where); current_line = where - 1; } @@ -481,7 +358,7 @@ static void opmap_insert(struct op_s *op) 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); + ERROR("hash map collision %lu: %s VS %s", hash, op->op, op_map[hash].op); memcpy(op_map+hash, op, sizeof(*op)); } @@ -519,7 +396,7 @@ static int nopstack; static void push_opstack(const struct op_s *op) { if(nopstack>MAXOPSTACK - 1) { - error("operator stack overflow"); + ERROR("operator stack overflow"); } opstack[nopstack++] = op; } @@ -527,7 +404,7 @@ static void push_opstack(const struct op_s *op) static const struct op_s *pop_opstack(void) { if(!nopstack) { - error("operator stack empty"); + ERROR("operator stack empty"); } return opstack[--nopstack]; } @@ -600,7 +477,7 @@ static void shunt_op(const struct op_s *op) if(!(pop = pop_opstack()) || strcmp(pop->op,"(") != 0) { - error("mismatched parentheses"); + ERROR("mismatched parentheses"); } return; } @@ -659,7 +536,7 @@ static vartype eval_expr(char *str) } else if(strcmp(op->op, "(") != 0 && !op->unary) { - error("illegal use of binary operator (%s)", op->op); + ERROR("illegal use of binary operator (%s)", op->op); } } shunt_op(op); @@ -669,7 +546,7 @@ static vartype eval_expr(char *str) tstart = expr; else if(!isSpace(*expr)) { - error("syntax error"); + ERROR("syntax ERROR"); } } else @@ -719,7 +596,7 @@ static vartype eval_expr(char *str) } else if(!isValidNumberOrVariable(expr)) { - error("syntax error"); + ERROR("syntax ERROR"); } } } @@ -766,14 +643,14 @@ static int let_handler(char **save) if(tok) eval_expr(tok); else - error("exprected valid expression after LET"); + ERROR("exprected valid expression after LET"); write_instr(POP); write_varid(varid); } else { - error("invalid variable name for LET"); + ERROR("invalid variable name for LET"); } return OK; } @@ -791,7 +668,7 @@ static int repeat_handler(char **save) return OK; } else - error("expected valid expression after REPEAT"); + ERROR("expected valid expression after REPEAT"); } static int goto_handler(char **save) @@ -805,7 +682,7 @@ static int goto_handler(char **save) return OK; } else - error("expected valid expression after GOTO"); + ERROR("expected valid expression after GOTO"); } static int call_handler(char **save) @@ -820,7 +697,7 @@ static int call_handler(char **save) return OK; } else - error("expected destination for CALL"); + ERROR("expected destination for CALL"); } static int ret_handler(char **save) @@ -857,7 +734,7 @@ static int if_handler(char **save) char *tok = strtok_r(NULL, ";", save); if(!tok) - error("expected conditional after IF"); + ERROR("expected conditional after IF"); eval_expr(tok); write_instr(PUSHIMM); @@ -878,7 +755,7 @@ static int delay_handler(char **save) write_instr(DELAY); } else - error("expected valid expression after DELAY"); + ERROR("expected valid expression after DELAY"); return OK; } @@ -888,7 +765,7 @@ static int log_handler(char **save) (void) save; char *tok = strtok_r(NULL, "", save); - vid_write(tok); + write_str(tok); return OK; } @@ -903,7 +780,7 @@ static int logvar_handler(char **save) return OK; } else - error("expected expression after LOGVAR"); + ERROR("expected expression after LOGVAR"); } static int rem_handler(char **save) @@ -936,7 +813,7 @@ static int logchar_handler(char **save) write_instr(LOGASCII); } else - error("expected expression after LOGCHAR"); + ERROR("expected expression after LOGCHAR"); return OK; } @@ -954,7 +831,7 @@ static int input_handler(char **save) } else { - error("invalid variable name for INPUT"); + ERROR("invalid variable name for INPUT"); } return OK; } @@ -967,14 +844,14 @@ static int prompt_handler(char **save) if(varname && isValidVariable(varname)) { int varid = get_varid(varname); - vid_write(varname); - vid_write("? "); + write_str(varname); + write_str("? "); write_instr(READ_VAR); write_varid(varid); } else { - error("invalid variable name for PROMPT"); + ERROR("invalid variable name for PROMPT"); } return OK; } @@ -1039,7 +916,7 @@ static int dfldel_handler(char **save) write_instr(DFL_DELAY); } else - error("expected expression for DEFAULT_DELAY"); + ERROR("expected expression for DEFAULT_DELAY"); return OK; } @@ -1052,7 +929,7 @@ static int strdel_handler(char **save) write_instr(STR_DELAY); } else - error("expected expression for STRING_DELAY"); + ERROR("expected expression for STRING_DELAY"); return OK; } @@ -1066,7 +943,7 @@ static int out_handler(char **save) write_instr(TYPE_DEC); } else - error("expected expression for default delay"); + ERROR("expected expression for default delay"); return OK; } @@ -1639,7 +1516,7 @@ 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 %s %s", tok->tok, tokmap[hash].tok); + ERROR("FIXME: hash map collision %s %s", tok->tok, tokmap[hash].tok); memcpy(tokmap+hash, tok, sizeof(*tok)); } @@ -1660,6 +1537,35 @@ static void init_globals(void) bytes_written = 0; } +/* depends on index_lines's output, indexes labels in the file */ +void index_labels(int fd) +{ + for(unsigned i = 1; i <= num_lines; ++i) + { + lseek(fd, line_offset[i], SEEK_SET); + char buf[MAX_LINE_LEN]; + int status = read_line(fd, buf, sizeof(buf)); + + /* exit early if failed or too short for a label */ + if(status < strlen("LBL")) + 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, i); + setConst(tok, true); + } + } + } + lseek(fd, 0, SEEK_SET); +} + int ducky_compile(int fd, bool verbose, int out) { if(!setjmp(exit_point)) @@ -1677,12 +1583,12 @@ int ducky_compile(int fd, bool verbose, int out) atexit(exit_handler); if(file_des < 0) - error("invalid file"); + ERROR("invalid file"); init_optable(); init_tokmap(); - line_offset = index_lines(file_des, &num_lines); + line_offset = index_lines(file_des, &num_lines, false, isValidVariable, setConst, setVariable); write_imm(DUCKY_MAGIC); write_imm(num_lines); off_t linetab_off = bytes_written; @@ -1703,6 +1609,11 @@ int ducky_compile(int fd, bool verbose, int out) makeConstantVariable("true", 1); makeConstantVariable("false", 0); + /* initialize the special variables */ + setSpecial("NULL", SPECIAL_NULL); + setSpecial("RAND", SPECIAL_RAND); + setSpecial("TIME", SPECIAL_TIME); + /* initialize labels (using output from index_lines) */ index_labels(file_des); @@ -1750,7 +1661,7 @@ int ducky_compile(int fd, bool verbose, int out) case NEXT: goto next_line; default: - error("FIXME: invalid return value"); + ERROR("FIXME: invalid return value"); } #ifdef DUCKY_ROCKBOX else if(strlen(tok) == 1) @@ -1763,7 +1674,7 @@ int ducky_compile(int fd, bool verbose, int out) goto next_line; else { - error("unknown token `%s` on line %d %d", tok, current_line); + ERROR("unknown token `%s` on line %d %d", tok, current_line); goto done; } } while(tok); diff --git a/src/ducky.h b/src/ducky.h index 1552134..2fda55d 100644 --- a/src/ducky.h +++ b/src/ducky.h @@ -1,10 +1,20 @@ #include <stdbool.h> #include <stdint.h> +#include "platform.h" #define DUCKY_MAGIC 0x4475634B /* DucK */ #define VARFORMAT "%d" #define VARNAME_MAX 24 + +#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 for rockbox cross-compile */ #define DUCKY_ROCKBOX @@ -15,12 +25,26 @@ #define OK 0 -int ducky_interp(int fd, bool verbose); -int ducky_compile(int fd, bool verbose, int out_fd); -int ducky_vm(int fd); -int ducky_to_c(int fd, int out_fd); +enum special_id { SPECIAL_NULL = 0, SPECIAL_RAND, SPECIAL_TIME }; typedef int32_t imm_t; typedef uint8_t instr_t; typedef uint16_t varid_t; typedef imm_t vartype; + +int ducky_interp(int fd, bool verbose); +int ducky_compile(int fd, bool verbose, int out_fd); +int ducky_vm(int fd); +int ducky_to_c(int fd, int out_fd); + +int read_line(int fd, char *buf, size_t sz); + +off_t *index_lines(int fd, unsigned *numlines, bool labels, bool (*isValidVariable)(const char*), void (*setConst)(const char*, bool), void (*setVariable)(const char*, vartype)); + +void vid_logf(const char *fmt, ...) __attribute__((format(printf,1,2))); + +void error(jmp_buf exit_point, int current_line, const char *fmt, ...) __attribute__((noreturn,format(printf,3,4))); + +#define ERROR(fmt, ...) (error(exit_point, current_line, fmt, ##__VA_ARGS__)) +void warning_real(int current_line, const char *fmt, ...) __attribute__((format(printf,2,3))); +#define WARNING(fmt, ...) (warning_real(current_line, fmt, ##__VA_ARGS__)) diff --git a/src/emitc.c b/src/emitc.c index 6de0401..94d8210 100644 --- a/src/emitc.c +++ b/src/emitc.c @@ -23,9 +23,7 @@ static int repeats_left; static imm_t repeat_line; static jmp_buf exit_point; -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 instr_t read_instr(void) { @@ -133,21 +131,6 @@ static void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...) 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_writef("Line %d: ", current_line); - vid_writef("ERROR: %s\n", fmtbuf); - va_end(ap); - - longjmp(exit_point, 1); -} - static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...) { char fmtbuf[256]; @@ -220,7 +203,7 @@ static void repeat_handler(void) write_src("if(repeats_left > 0)\n"); write_src("{\n"); write_src("if(repeat_line + 1 != vars[0].val)\n"); - write_src("error(\"nested REPEAT\");\n"); + write_src("ERROR(\"nested REPEAT\");\n"); write_src("--repeats_left;\n"); write_src("if(repeats_left > 0)\n"); write_src("{\n"); @@ -248,7 +231,7 @@ static void subcall_handler(void) write_src("JUMP(POP());\n"); write_src("}\n"); write_src("else\n"); - write_src("error(\"call stack overflow\");\n"); + write_src("ERROR(\"call stack overflow\");\n"); } static void subret_handler(void) @@ -258,7 +241,7 @@ static void subret_handler(void) write_src("JUMP(callstack[--callstack_pointer]);\n"); write_src("}\n"); write_src("else\n"); - write_src("error(\"call stack underflow\");\n"); + write_src("ERROR(\"call stack underflow\");\n"); } static void if_handler(void) @@ -335,7 +318,7 @@ static void mod_handler(void) write_src("{\n"); write_src("imm_t b = POP();\n"); write_src("imm_t a = POP();\n"); - write_src("PUSH(a%b);\n"); + write_src("PUSH(a%%b);\n"); write_src("}\n"); } @@ -511,6 +494,14 @@ static void input_handler(void) write_src("}\n"); } +static void mkspecial_handler(void) +{ + varid_t varid = read_varid(); + write_src("vars[%d].constant = true;\n", varid); + write_src("vars[%d].type = TYPE_SPECIAL;\n", varid); + write_src("vars[%d].special = %d;\n", varid, read_imm()); +} + static void inc_line_pointer(void) { ++current_line; @@ -568,7 +559,7 @@ static void (*instr_tab[0x100])(void) = { NULL, /* 0x2d */ newline_handler, /* 0x2e */ input_handler, /* 0x2f */ - NULL, /* 0x30 */ + mkspecial_handler, /* 0x30 */ NULL, /* 0x31 */ NULL, /* 0x32 */ NULL, /* 0x33 */ @@ -787,7 +778,8 @@ void write_stub_code(int num_lines) write_src("#include <stdio.h>\n"); write_src("#include <stdint.h>\n"); write_src("#include <stdlib.h>\n\n"); - write_src("#include <math.h>\n\n"); + write_src("#include <math.h>\n"); + write_src("#include <time.h>\n\n"); write_src("#define CALLSTACK_SZ %d\n", CALLSTACK_SZ); write_src("#define MAX_VARS %d\n", MAX_VARS); write_src("#define STACK_SZ %d\n\n", STACK_SZ); @@ -797,7 +789,7 @@ void write_stub_code(int num_lines) write_src("imm_t stack[STACK_SZ];\n"); write_src("imm_t callstack[CALLSTACK_SZ];\n"); write_src("unsigned repeats_left = 0, repeat_line = 0;\n"); - write_src("struct var_t { imm_t val; bool constant; };\n"); + write_src("struct var_t { enum {TYPE_PLAIN, TYPE_SPECIAL} type; bool constant; union { vartype val; int special;}; };\n"); write_src("struct var_t vars[MAX_VARS];\n\n"); write_src("static inline void vid_write(const char *str)\n"); @@ -815,7 +807,7 @@ void write_stub_code(int num_lines) write_src("va_end(ap);\n"); write_src("}\n"); - write_src("static void __attribute__((noreturn,format(printf,1,2))) error(const char *fmt, ...)\n"); + write_src("static void __attribute__((noreturn,format(printf,1,2))) ERROR(const char *fmt, ...)\n"); write_src("{\n"); write_src("char fmtbuf[256];\n"); write_src("va_list ap;\n"); @@ -828,13 +820,45 @@ void write_stub_code(int num_lines) write_src("exit(EXIT_FAILURE);\n"); write_src("}\n"); + write_src("vartype spec_zero(void)\n"); + write_src("{\n"); + write_src("return 0;\n"); + write_src("}\n"); + + write_src("vartype spec_rand(void)\n"); + write_src("{\n"); + write_src("return rand();\n"); + write_src("}\n"); + + write_src("vartype spec_time(void)\n"); + write_src("{\n"); + write_src("return time(NULL);\n"); + write_src("}\n"); + + write_src("vartype (*special_funcs[])(void) = "); + write_src("{\n"); + write_src("spec_zero,\n"); + write_src("spec_rand,\n"); + write_src("spec_time,\n"); + write_src("};\n"); + + write_src("#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))\n"); + + write_src("vartype get_special(int special)\n"); + write_src("{\n"); + write_src("if(special < ARRAYLEN(special_funcs))\n"); + write_src("return special_funcs[special]();\n"); + write_src("else\n"); + write_src("ERROR(\"unknown special stream\");\n"); + write_src("}\n"); + #if 0 write_src("static inline void PUSH(imm_t n)\n"); write_src("{\n"); write_src("if(stack_pointer < STACK_SZ)\n"); write_src("stack[stack_pointer++] = n;\n"); write_src("else\n"); - write_src("error(\"stack overflow\");\n"); + write_src("ERROR(\"stack overflow\");\n"); write_src("}\n"); write_src("static inline imm_t POP(void)\n"); @@ -842,16 +866,20 @@ void write_stub_code(int num_lines) write_src("if(stack_pointer > 0)\n"); write_src("return stack[--stack_pointer];\n"); write_src("else\n"); - write_src("error(\"stack underflow\");\n"); + write_src("ERROR(\"stack underflow\");\n"); write_src("}\n"); #endif write_src("static inline vartype getvar(varid_t varid)\n"); write_src("{\n"); write_src("if(varid < %d)\n", MAX_VARS); + write_src("{\n"); + write_src("struct var_t *var = vars+varid;\n"); + write_src("if(var->type == TYPE_PLAIN)\n"); write_src("return vars[varid].val;\n"); write_src("else\n"); - write_src("error(\"cannot access variable\");\n"); + write_src("return get_special(vars[varid].special);\n"); + write_src("}\n"); write_src("}\n"); write_src("static inline void setvar(varid_t varid, vartype val)\n"); @@ -859,7 +887,7 @@ void write_stub_code(int num_lines) write_src("if(varid < %d && !vars[varid].constant)\n", MAX_VARS); write_src("vars[varid].val = val;\n"); write_src("else\n"); - write_src("error(\"cannot modify variable\");\n"); + write_src("ERROR(\"cannot modify variable\");\n"); write_src("}\n"); write_src("static inline void mkconst(varid_t varid)\n"); @@ -873,8 +901,8 @@ void write_stub_code(int num_lines) write_src("return a2<0 ? 0 : (a2==0?1:a1*eval_exp(a1, a2-1));\n"); write_src("}\n"); - write_src("#define JUMP(LINE) do{imm_t x = LINE; if(1 <= x && x <= %d) goto *jump_table[x]; else error(\"jump target out of range\");}while(0);\n\n", num_lines); - write_src("#define PUSH(VAL) do { imm_t x = VAL; if(stack_pointer < STACK_SZ) stack[stack_pointer++] = x; else error(\"stack overflow\");}while(0);\n\n"); + write_src("#define JUMP(LINE) do{imm_t x = LINE; if(1 <= x && x <= %d) goto *jump_table[x]; else ERROR(\"jump target out of range\");}while(0);\n\n", num_lines); + write_src("#define PUSH(VAL) do { imm_t x = VAL; if(stack_pointer < STACK_SZ) stack[stack_pointer++] = x; else ERROR(\"stack overflow\");}while(0);\n\n"); write_src("#define POP() (stack[--stack_pointer])\n"); write_src("int main()\n"); @@ -882,6 +910,8 @@ void write_stub_code(int num_lines) write_src("register imm_t stack_pointer = 0, callstack_pointer = 0;\n"); write_src("(void) stack_pointer; (void) callstack_pointer;\n"); write_src("(void) eval_exp;\n"); + /* prefetch some high-use variables */ + write_src("/* this uses labels as values, a GCC extension */\n"); write_src("const void *jump_table[%d] = {\n", num_lines + 1); @@ -905,7 +935,7 @@ int ducky_to_c(int fd, int out) init_globals(); if(read_imm() != DUCKY_MAGIC) - error("unknown format"); + ERROR("unknown format"); num_lines = read_imm(); for(unsigned int i = 1; i <= num_lines; ++i) @@ -925,7 +955,7 @@ int ducky_to_c(int fd, int out) if(handler) handler(); else - error("invalid instruction %d", instr); + ERROR("invalid instruction %d", instr); } write_src("}"); return 0; diff --git a/src/interp.c b/src/interp.c index c49d13e..1ee91c3 100644 --- a/src/interp.c +++ b/src/interp.c @@ -28,18 +28,6 @@ /*** 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 VARNAME_MAX 24 - /*** Globals ***/ static off_t *line_offset = NULL; @@ -57,12 +45,17 @@ static jmp_buf exit_point; struct varnode_t { char name[VARNAME_MAX + 1]; - vartype val; - bool constant; /* used by setVariable */ + enum { TYPE_PLAIN, TYPE_SPECIAL } type; + bool constant; + union { + vartype val; + struct { + enum special_id special; + }; + }; struct varnode_t *next; }; -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))); @@ -108,6 +101,7 @@ static struct varnode_t *lookup_var(const char *name) strncpy(new->name, name, sizeof(new->name)); new->val = 0; new->constant = false; + new->type = TYPE_PLAIN; new->next = NULL; if(!last) @@ -120,7 +114,13 @@ static struct varnode_t *lookup_var(const char *name) static vartype getVariable(const char *name) { - return lookup_var(name)->val; + struct varnode_t *node = lookup_var(name); + if(node->type == TYPE_PLAIN) + return node->val; + else if(node->type == TYPE_SPECIAL) + return get_special(node->special); + else + ERROR("unknown type"); } static void setVariable(const char *name, vartype val) @@ -129,7 +129,7 @@ static void setVariable(const char *name, vartype val) if(!node->constant) node->val = val; else - error("attempted to modify a constant variable"); + ERROR("attempted to modify a constant variable"); } static void setConst(const char *name, bool c) @@ -137,13 +137,21 @@ static void setConst(const char *name, bool c) lookup_var(name)->constant = c; } +static void setSpecial(const char *name, enum special_id spec) +{ + struct varnode_t *node = lookup_var(name); + node->type = TYPE_SPECIAL; + node->special = spec; + node->constant = true; +} + static 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"); + ERROR("attempted to modify a constant variable"); } static void decVar(const char *name) @@ -152,7 +160,7 @@ static void decVar(const char *name) if(!node->constant) --lookup_var(name)->val; else - error("attempted to modify a constant variable"); + ERROR("attempted to modify a constant variable"); } /*** Utility functions ***/ @@ -193,21 +201,6 @@ static void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...) 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_writef("Line %d: ", current_line); - vid_writef("ERROR: %s\n", fmtbuf); - va_end(ap); - - longjmp(exit_point, 1); -} - static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...) { char fmtbuf[256]; @@ -219,79 +212,6 @@ static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...) 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); - lookup_var(tok)->constant = 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) @@ -299,7 +219,7 @@ static void jump_line(int fd, unsigned where) lseek(fd, line_offset[where], SEEK_SET); } else - error("JUMP target out of range (%u)", where); + ERROR("JUMP target out of range (%u)", where); current_line = where - 1; } @@ -312,7 +232,7 @@ static void sub_call(int fd, unsigned where) jump_line(fd, where); } else - error("call stack overflow"); + ERROR("call stack overflow"); } static void sub_return(int fd) @@ -344,14 +264,14 @@ static vartype eval_mul(vartype a1, vartype a2) static vartype eval_div(vartype a1, vartype a2) { if(!a2) { - error("division by zero"); + ERROR("division by zero"); } return a1/a2; } static vartype eval_mod(vartype a1, vartype a2) { if(!a2) { - error("division by zero"); + ERROR("division by zero"); } return a1%a2; } @@ -525,7 +445,7 @@ static void opmap_insert(struct op_s *op) 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); + ERROR("hash map collision %lu: %s VS %s", hash, op->op, op_map[hash].op); memcpy(op_map+hash, op, sizeof(*op)); } @@ -566,7 +486,7 @@ static int nnumstack; static void push_opstack(const struct op_s *op) { if(nopstack>MAXOPSTACK - 1) { - error("operator stack overflow"); + ERROR("operator stack overflow"); } opstack[nopstack++] = op; } @@ -574,7 +494,7 @@ static void push_opstack(const struct op_s *op) static const struct op_s *pop_opstack(void) { if(!nopstack) { - error("operator stack empty"); + ERROR("operator stack empty"); } return opstack[--nopstack]; } @@ -582,7 +502,7 @@ static const struct op_s *pop_opstack(void) static void push_numstack(vartype num) { if(nnumstack>MAXNUMSTACK - 1) { - error("number stack overflow"); + ERROR("number stack overflow"); } numstack[nnumstack++] = num; } @@ -590,7 +510,7 @@ static void push_numstack(vartype num) static vartype pop_numstack(void) { if(!nnumstack) { - error("number stack empty"); + ERROR("number stack empty"); } return numstack[--nnumstack]; } @@ -684,7 +604,7 @@ static void shunt_op(const struct op_s *op) if(!(pop = pop_opstack()) || strcmp(pop->op,"(") != 0) { - error("mismatched parentheses"); + ERROR("mismatched parentheses"); } return; } @@ -760,7 +680,7 @@ static vartype eval_expr(char *str) } else if(strcmp(op->op, "(") != 0 && !op->unary) { - error("illegal use of binary operator (%s)", op->op); + ERROR("illegal use of binary operator (%s)", op->op); } } shunt_op(op); @@ -770,7 +690,7 @@ static vartype eval_expr(char *str) tstart = expr; else if(!isSpace(*expr)) { - error("syntax error"); + ERROR("syntax ERROR"); } } else @@ -790,7 +710,7 @@ static vartype eval_expr(char *str) } else if(!isValidNumberOrVariable(expr)) { - error("syntax error"); + ERROR("syntax ERROR"); } } } @@ -811,7 +731,7 @@ static vartype eval_expr(char *str) } if(nnumstack != 1) { - error("invalid expression"); + ERROR("invalid expression"); } return numstack[0]; @@ -831,17 +751,17 @@ static int let_handler(char **save, int *repeats_left) { struct varnode_t *node = lookup_var(varname); if(node->constant) - error("attempted to modify a constant variable"); + 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"); + ERROR("exprected valid expression after LET"); } else { - error("invalid variable name for LET"); + ERROR("invalid variable name for LET"); } return OK; } @@ -852,7 +772,7 @@ static int repeat_handler(char **save, int *repeats_left) if(*repeats_left > 0) { if(repeat_line + 1 != current_line) - error("nested REPEAT"); + ERROR("nested REPEAT"); --(*repeats_left); if(*repeats_left) jump_line(file_des, repeat_line); @@ -863,7 +783,7 @@ static int repeat_handler(char **save, int *repeats_left) if(tok) { if(current_line == 1) - error("REPEAT without a previous instruction"); + ERROR("REPEAT without a previous instruction"); *repeats_left = eval_expr(tok) - 1; if(*repeats_left) { @@ -872,7 +792,7 @@ static int repeat_handler(char **save, int *repeats_left) } } else - error("expected valid expression after REPEAT"); + ERROR("expected valid expression after REPEAT"); } return NEXT; } @@ -887,7 +807,7 @@ static int goto_handler(char **save, int *repeats_left) return NEXT; } else - error("expected valid expression after GOTO"); + ERROR("expected valid expression after GOTO"); } static int call_handler(char **save, int *repeats_left) @@ -900,7 +820,7 @@ static int call_handler(char **save, int *repeats_left) return NEXT; } else - error("expected destination for CALL"); + ERROR("expected destination for CALL"); } static int ret_handler(char **save, int *repeats_left) @@ -934,7 +854,7 @@ static int if_handler(char **save, int *repeats_left) char *tok = strtok_r(NULL, ";", save); if(!tok) - error("expected conditional after IF"); + ERROR("expected conditional after IF"); /* break out of the do-while if the condition is false */ if(!eval_expr(tok)) @@ -958,7 +878,7 @@ static int delay_handler(char **save, int *repeats_left) nanosleep(&t, NULL); } else - error("expected valid expression after DELAY"); + ERROR("expected valid expression after DELAY"); return OK; } @@ -982,7 +902,7 @@ static int logvar_handler(char **save, int *repeats_left) return OK; } else - error("expected expression after LOGVAR"); + ERROR("expected expression after LOGVAR"); } static int rem_handler(char **save, int *repeats_left) @@ -1014,7 +934,7 @@ static int logchar_handler(char **save, int *repeats_left) return OK; } else - error("expected expression after LOGCHAR"); + ERROR("expected expression after LOGCHAR"); } @@ -1027,12 +947,12 @@ static int input_handler(char **save, int *repeats_left) { struct varnode_t *node = lookup_var(varname); if(node->constant) - error("attempted to modify a constant variable"); + ERROR("attempted to modify a constant variable"); scanf(VARFORMAT, &node->val); } else - error("expected valid variable name after INPUT"); + ERROR("expected valid variable name after INPUT"); return OK; } @@ -1046,12 +966,12 @@ static int prompt_handler(char **save, int *repeats_left) vid_writef("%s? ", varname); struct varnode_t *node = lookup_var(varname); if(node->constant) - error("attempted to modify a constant variable"); + ERROR("attempted to modify a constant variable"); scanf(VARFORMAT, &node->val); } else - error("expected valid variable name after PROMPT"); + ERROR("expected valid variable name after PROMPT"); return OK; } @@ -1248,7 +1168,7 @@ 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"); + ERROR("FIXME: hash map collision"); memcpy(tokmap+hash, tok, sizeof(*tok)); } @@ -1289,7 +1209,7 @@ int ducky_interp(int fd, bool verbose) atexit(exit_handler); if(file_des < 0) - error("invalid file"); + ERROR("invalid file"); init_optable(); init_tokmap(); @@ -1307,7 +1227,13 @@ int ducky_interp(int fd, bool verbose) setVariable("false", 0); setConst("false", true); - line_offset = index_lines(file_des, &num_lines); + /* initialize the special variables */ + setSpecial("NULL", SPECIAL_NULL); + setSpecial("RAND", SPECIAL_RAND); + setSpecial("TIME", SPECIAL_TIME); + + line_offset = index_lines(file_des, &num_lines, true, isValidVariable, setConst, setVariable); + if(verbose) { vid_writef("Indexing complete (%u lines).", num_lines); @@ -1356,13 +1282,13 @@ int ducky_interp(int fd, bool verbose) case NEXT: goto next_line; default: - error("FIXME: invalid return value"); + ERROR("FIXME: invalid return value"); } else if(tok[0] == '#') goto next_line; else { - error("unknown token `%s` on line %d %d", tok, current_line); + ERROR("unknown token `%s` on line %d %d", tok, current_line); goto done; } } while(tok); diff --git a/src/opcodes.h b/src/opcodes.h index 521afcf..2a0288c 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -46,5 +46,6 @@ #define ADD_CHAR 0x2D #define NEWLINE 0x2E #define READ_VAR 0x2F +#define MKSPECIAL 0x30 /* new opcodes here, be sure to update compile.c, vm.c, and emitc.c */ #define LINEMARK 0xFF @@ -15,7 +15,15 @@ static unsigned stack_pointer; static jmp_buf exit_point; struct var_t { - vartype val; + enum { TYPE_PLAIN, TYPE_SPECIAL } type; + + union { + vartype val; + struct { + enum special_id special; + }; + }; + bool constant; }; @@ -29,9 +37,7 @@ static bool want_quit; static int repeats_left; static imm_t repeat_line; -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))); instr_t read_instr(void) { @@ -81,21 +87,6 @@ static void __attribute__((format(printf,1,2))) vid_writef(const char *fmt, ...) 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_writef("Line %d: ", current_line); - vid_writef("ERROR: %s\n", fmtbuf); - va_end(ap); - - longjmp(exit_point, 1); -} - static void __attribute__((format(printf,1,2))) warning(const char *fmt, ...) { char fmtbuf[256]; @@ -122,7 +113,7 @@ static inline void push(imm_t n) if(stack_pointer < STACK_SZ) stack[stack_pointer++] = n; else - error("stack overflow"); + ERROR("stack overflow"); } static inline imm_t pop(void) @@ -130,13 +121,19 @@ static inline imm_t pop(void) if(stack_pointer > 0) return stack[--stack_pointer]; else - error("stack underflow"); + ERROR("stack underflow"); } static inline vartype getvar(varid_t varid) { if(varid < ARRAYLEN(vars)) - return vars[varid].val; + { + struct var_t *var = vars+varid; + if(var->type == TYPE_PLAIN) + return vars[varid].val; + else + return get_special(vars[varid].special); + } } static inline void setvar(varid_t varid, vartype val) @@ -144,7 +141,7 @@ static inline void setvar(varid_t varid, vartype val) if(varid < ARRAYLEN(vars) && !vars[varid].constant) vars[varid].val = val; else - error("cannot modify variable"); + ERROR("cannot modify variable"); } static inline void mkconst(varid_t varid) @@ -161,7 +158,7 @@ static inline void jump(imm_t line) current_line = line; } else - error("jump target out of bounds"); + ERROR("jump target out of bounds"); } static void pushimm_handler(void) @@ -215,7 +212,7 @@ static void repeat_handler(void) if(repeats_left > 0) { if(repeat_line + 1 != current_line) - error("nested REPEAT"); + ERROR("nested REPEAT"); --repeats_left; if(repeats_left > 0) { @@ -243,7 +240,7 @@ static void subcall_handler(void) jump(pop()); } else - error("call stack overflow"); + ERROR("call stack overflow"); } static void subret_handler(void) @@ -253,7 +250,7 @@ static void subret_handler(void) jump(callstack[--callstack_pointer]); } else - error("call stack underflow"); + ERROR("call stack underflow"); } static void if_handler(void) @@ -459,13 +456,6 @@ static void newline_handler(void) vid_writef("\n"); } -static void inc_line_pointer(void) -{ - ++current_line; - - vars[0].val = current_line; -} - static void input_handler(void) { vartype val; @@ -473,6 +463,21 @@ static void input_handler(void) setvar(read_varid(), val); } +static void mkspecial_handler(void) +{ + varid_t varid = read_varid(); + vars[varid].constant = true; + vars[varid].type = TYPE_SPECIAL; + vars[varid].special = read_imm(); +} + +static void inc_line_pointer(void) +{ + ++current_line; + + vars[0].val = current_line; +} + static void (*instr_tab[0x100])(void) = { pushimm_handler, /* 0x0 */ pushvar_handler, /* 0x1 */ @@ -522,7 +527,7 @@ static void (*instr_tab[0x100])(void) = { NULL, /* 0x2d */ newline_handler, /* 0x2e */ input_handler, /* 0x2f */ - NULL, /* 0x30 */ + mkspecial_handler, /* 0x30 */ NULL, /* 0x31 */ NULL, /* 0x32 */ NULL, /* 0x33 */ @@ -741,7 +746,7 @@ int ducky_vm(int fd) init_globals(); if(read_imm() != DUCKY_MAGIC) - error("unknown format"); + ERROR("unknown format"); num_lines = read_imm(); line_offset = malloc(num_lines + 1); @@ -760,7 +765,7 @@ int ducky_vm(int fd) if(handler) handler(); else - error("invalid instruction"); + ERROR("invalid instruction"); } return 0; } |