#include #include #include #include #include #include #define UNUSED(x) (void)x #define DEF(x) \ static void exec_##x(struct insn i, size_t sp, struct vec *stack, \ struct vec *globals) static int64_t load(struct loc l, size_t sp, struct vec *stack, struct vec *globals) { if (l.l) return vect_at(int64_t, *stack, l.o + sp); return vect_at(int64_t, *globals, l.o); } static void store(struct loc l, int64_t v, size_t sp, struct vec *stack, struct vec *globals) { if (l.l) { vect_at(int64_t, *stack, l.o + sp) = v; return; } vect_at(int64_t, *globals, l.o) = v; } #define get(l) \ load(l, sp, stack, globals) #define put(l, v) \ store(l, v, sp, stack, globals) DEF(MOVE) { int64_t i0 = get(i.i0); put(i.o, i0); } DEF(ADD) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t o = i0 + i1; put(i.o, o); } DEF(SUB) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t o = i0 - i1; put(i.o, o); } DEF(MUL) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t o = i0 * i1; put(i.o, o); } DEF(DIV) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t o = i0 / i1; put(i.o, o); } DEF(PRINT_DATE) { int64_t i0 = get(i.i0); char str[11] = {0}; date_to_string(str, i0); printf("%s", str); } DEF(PRINT_INT) { int64_t i0 = get(i.i0); printf("%lli", (long long)i0); } DEF(PRINT_STRING) { int64_t i0 = get(i.i0); printf("%s", (const char *)i0); } DEF(PRINT_BOOL) { int64_t i0 = get(i.i0); printf("%s", i0 ? "true" : "false"); } DEF(PRINT_NEWLINE) { UNUSED(i); UNUSED(sp); UNUSED(stack); UNUSED(globals); printf("\n"); } DEF(PRINT_SPACE) { UNUSED(i); UNUSED(sp); UNUSED(stack); UNUSED(globals); printf(" "); } DEF(CONST) { put(i.o, i.v); } DEF(EQ) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t b = i0 == i1; put(i.o, b); } DEF(LT) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); int64_t b = i0 < i1; put(i.o, b); } DEF(NEG) { int64_t i0 = get(i.i0); put(i.o, -i0); } DEF(LOAD_DAY) { int64_t i0 = get(i.i0); unsigned day = 0; date_split((ph_date_t)i0, NULL, NULL, &day); put(i.o, (int64_t)day); } DEF(LOAD_MONTH) { int64_t i0 = get(i.i0); unsigned month = 0; date_split((ph_date_t)i0, NULL, &month, NULL); put(i.o, (int64_t)month); } DEF(LOAD_YEAR) { int64_t i0 = get(i.i0); unsigned year = 0; date_split((ph_date_t)i0, &year, NULL, NULL); put(i.o, (int64_t)year); } DEF(LOAD_WEEKDAY) { int64_t i0 = get(i.i0); struct tm time = tm_from_date((ph_date_t)i0); const char *day = "Sunday"; switch (time.tm_wday) { case 0: day = "Sunday"; break; case 1: day = "Monday"; break; case 2: day = "Tuesday"; break; case 3: day = "Wednesday"; break; case 4: day = "Thursday"; break; case 5: day = "Friday"; break; case 6: day = "Saturday"; break; } put(i.o, (int64_t)day); } DEF(LOAD_WEEKNUM) { int64_t i0 = get(i.i0); struct tm time = tm_from_date((ph_date_t)i0); put(i.o, time.tm_yday / 7); } DEF(STORE_DAY) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); unsigned year = 0; unsigned month = 0; date_split((ph_date_t)i0, &year, &month, NULL); ph_date_t date = date_from_numbers(year, month, i1); put(i.o, (int64_t)date); } DEF(STORE_MONTH) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); unsigned year = 0; unsigned day = 0; date_split((ph_date_t)i0, &year, NULL, &day); ph_date_t date = date_from_numbers(year, i1, day); put(i.o, (int64_t)date); } DEF(STORE_YEAR) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); unsigned month = 0; unsigned day = 0; date_split((ph_date_t)i0, NULL, &month, &day); ph_date_t date = date_from_numbers(i1, month, day); put(i.o, (int64_t)date); } DEF(TODAY) { ph_date_t date = current_date(); put(i.o, (int64_t)date); } DEF(DATE_ADD) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); struct tm time = tm_from_date((ph_date_t)i0); time.tm_mday += i1; mktime(&time); ph_date_t date = date_from_tm(time); put(i.o, (int64_t)date); } DEF(DATE_SUB) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); struct tm time = tm_from_date((ph_date_t)i0); time.tm_mday -= i1; mktime(&time); ph_date_t date = date_from_tm(time); put(i.o, (int64_t)date); } DEF(DATE_DIFF) { int64_t i0 = get(i.i0); int64_t i1 = get(i.i1); struct tm time0 = tm_from_date((ph_date_t)i0); struct tm time1 = tm_from_date((ph_date_t)i1); /* close enough at least */ time_t t0 = mktime(&time0); time_t t1 = mktime(&time1); double seconds = difftime(t0, t1); int64_t days = round(seconds / 86400); put(i.o, days); } static int64_t exec_func(struct fn *f, struct vec *args, size_t sp, struct vec *stack, struct vec *globals) { /* move args to formal locations */ size_t i = 0; foreach_vec(ai, *args) { int64_t a = vect_at(int64_t, *args, ai); vect_at(int64_t, *stack, sp + i) = a; i += 1; } vec_reset(args); size_t pc = 0; int64_t retval = 0; struct vec insns = f->insns; while (1) { struct insn i = vect_at(struct insn, insns, pc); #define DO(x) case x: exec_##x(i, sp, stack, globals); break; switch (i.k) { /* special cases first */ case LABEL: break; case STOP: return 0; case RET: return get(i.i0); case RETVAL: put(i.o, retval); break; case J: pc = i.v; continue; case B: { int64_t i0 = get(i.i0); if (i0) { pc = i.v; continue; } break; } case BZ: { int64_t i0 = get(i.i0); if (!i0) { pc = i.v; continue; } break; } case ARG: { int64_t a = get(i.i0); vect_append(int64_t, *args, &a); break; } case CALL: { struct fn *cf = find_fn(i.v); assert(cf); retval = exec_func(cf, args, sp + f->max_sp, stack, globals); break; } DO(MOVE); DO(ADD); DO(SUB); DO(MUL); DO(DIV); DO(CONST); DO(PRINT_DATE); DO(PRINT_INT); DO(PRINT_BOOL); DO(PRINT_STRING); DO(PRINT_NEWLINE); DO(PRINT_SPACE); DO(LOAD_DAY); DO(LOAD_MONTH); DO(LOAD_YEAR); DO(LOAD_WEEKDAY); DO(LOAD_WEEKNUM); DO(STORE_DAY); DO(STORE_MONTH); DO(STORE_YEAR); DO(DATE_ADD); DO(DATE_SUB); DO(DATE_DIFF); DO(TODAY); DO(EQ); DO(LT); DO(NEG); } #undef DO pc += 1; } } void execute() { struct fn *main = find_fn(0); assert(main); struct vec stack = vec_create(sizeof(int64_t)); /* arbitrary amount */ vec_reserve(&stack, 65535); struct vec globals = vec_create(sizeof(int64_t)); /* should really take the value calculated during lowering */ vec_reserve(&globals, 65535); struct vec args = vec_create(sizeof(int64_t)); exec_func(main, &args, 0, &stack, &globals); vec_destroy(&args); vec_destroy(&stack); vec_destroy(&globals); }