#include #include #include #include #include "execute.h" #include "opcode.h" #include "bytecode.h" #include "forkable_stack.h" #include "frame_layout.h" #include "jv_alloc.h" #include "locfile.h" #include "jv.h" #include "jv_aux.h" #include "parser.h" #include "builtin.h" typedef struct { jv value; int pathidx; } stackval; struct jq_state { struct forkable_stack data_stk; struct forkable_stack frame_stk; struct forkable_stack fork_stk; jv* pathbuf; int pathsize; // number of allocated elements }; int path_push(jq_state *jq, stackval sv, jv val) { int pos = sv.pathidx; assert(pos <= jq->pathsize); assert(pos >= 0); if (pos == jq->pathsize) { int oldpathsize = jq->pathsize; jq->pathsize = oldpathsize ? oldpathsize * 2 : 100; jq->pathbuf = jv_mem_realloc(jq->pathbuf, sizeof(jq->pathbuf[0]) * jq->pathsize); for (int i=oldpathsize; ipathsize; i++) { jq->pathbuf[i] = jv_invalid(); } } jv_free(jq->pathbuf[pos]); jq->pathbuf[pos] = val; return pos + 1; } stackval stackval_replace(stackval value, jv newjs) { jv_free(value.value); stackval s = {newjs, value.pathidx}; return s; } // Probably all uses of this function are bugs stackval stackval_root(jv v) { stackval s = {v, 0}; return s; } typedef struct { FORKABLE_STACK_HEADER; stackval sv; } data_stk_elem; void stack_push(jq_state *jq, stackval val) { assert(jv_is_valid(val.value)); data_stk_elem* s = forkable_stack_push(&jq->data_stk, sizeof(data_stk_elem)); s->sv = val; } stackval stack_pop(jq_state *jq) { data_stk_elem* s = forkable_stack_peek(&jq->data_stk); stackval sv = s->sv; if (!forkable_stack_pop_will_free(&jq->data_stk)) { sv.value = jv_copy(sv.value); } forkable_stack_pop(&jq->data_stk); assert(jv_is_valid(sv.value)); return sv; } struct forkpoint { FORKABLE_STACK_HEADER; struct forkable_stack_state saved_data_stack; struct forkable_stack_state saved_call_stack; }; void stack_save(jq_state *jq){ struct forkpoint* fork = forkable_stack_push(&jq->fork_stk, sizeof(struct forkpoint)); forkable_stack_save(&jq->data_stk, &fork->saved_data_stack); forkable_stack_save(&jq->frame_stk, &fork->saved_call_stack); } void stack_switch(jq_state *jq) { struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk); forkable_stack_switch(&jq->data_stk, &fork->saved_data_stack); forkable_stack_switch(&jq->frame_stk, &fork->saved_call_stack); } int stack_restore(jq_state *jq){ while (!forkable_stack_empty(&jq->data_stk) && forkable_stack_pop_will_free(&jq->data_stk)) { jv_free(stack_pop(jq).value); } while (!forkable_stack_empty(&jq->frame_stk) && forkable_stack_pop_will_free(&jq->frame_stk)) { frame_pop(&jq->frame_stk); } if (forkable_stack_empty(&jq->fork_stk)) { return 0; } else { struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk); forkable_stack_restore(&jq->data_stk, &fork->saved_data_stack); forkable_stack_restore(&jq->frame_stk, &fork->saved_call_stack); forkable_stack_pop(&jq->fork_stk); return 1; } } static struct closure make_closure(struct forkable_stack* stk, frame_ptr fr, uint16_t* pc) { uint16_t level = *pc++; uint16_t idx = *pc++; fr = frame_get_level(stk, fr, level); if (idx & ARG_NEWCLOSURE) { int subfn_idx = idx & ~ARG_NEWCLOSURE; assert(subfn_idx < frame_self(fr)->bc->nsubfunctions); struct closure cl = {frame_self(fr)->bc->subfunctions[subfn_idx], forkable_stack_to_idx(stk, fr)}; return cl; } else { return *frame_closure_arg(fr, idx); } } void print_error(jv value) { assert(!jv_is_valid(value)); jv msg = jv_invalid_get_msg(value); if (jv_get_kind(msg) == JV_KIND_STRING) { fprintf(stderr, "jq: error: %s\n", jv_string_value(msg)); } jv_free(msg); } #define ON_BACKTRACK(op) ((op)+NUM_OPCODES) jv jq_next(jq_state *jq) { jv cfunc_input[MAX_CFUNCTION_ARGS]; assert(!forkable_stack_empty(&jq->frame_stk)); uint16_t* pc = *frame_current_retaddr(&jq->frame_stk); frame_pop(&jq->frame_stk); assert(!forkable_stack_empty(&jq->frame_stk)); int backtracking = 0; while (1) { uint16_t opcode = *pc; #if JQ_DEBUG dump_operation(frame_current_bytecode(&jq->frame_stk), pc); printf("\t"); const struct opcode_description* opdesc = opcode_describe(opcode); data_stk_elem* param; int stack_in = opdesc->stack_in; if (stack_in == -1) stack_in = pc[1]; for (int i=0; idata_stk); } else { printf(" | "); param = forkable_stack_peek_next(&jq->data_stk, param); } if (!param) break; jv_dump(jv_copy(param->sv.value), 0); printf("<%d>", jv_get_refcnt(param->sv.value)); } if (backtracking) printf("\t"); printf("\n"); #endif if (backtracking) { opcode = ON_BACKTRACK(opcode); backtracking = 0; } pc++; switch (opcode) { default: assert(0 && "invalid instruction"); case LOADK: { jv v = jv_array_get(jv_copy(frame_current_bytecode(&jq->frame_stk)->constants), *pc++); assert(jv_is_valid(v)); stack_push(jq, stackval_replace(stack_pop(jq), v)); break; } case DUP: { stackval v = stack_pop(jq); stackval v2 = v; v2.value = jv_copy(v.value); stack_push(jq, v); stack_push(jq, v2); break; } case DUP2: { stackval keep = stack_pop(jq); stackval v = stack_pop(jq); stackval v2 = v; v2.value = jv_copy(v.value); stack_push(jq, v); stack_push(jq, keep); stack_push(jq, v2); break; } case SWAP: { stackval a = stack_pop(jq); stackval b = stack_pop(jq); stack_push(jq, a); stack_push(jq, b); break; } case POP: { jv_free(stack_pop(jq).value); break; } case APPEND: { // FIXME paths jv v = stack_pop(jq).value; uint16_t level = *pc++; uint16_t vidx = *pc++; frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level); jv* var = frame_local_var(fp, vidx); assert(jv_get_kind(*var) == JV_KIND_ARRAY); *var = jv_array_append(*var, v); break; } case INSERT: { stackval stktop = stack_pop(jq); jv v = stack_pop(jq).value; jv k = stack_pop(jq).value; stackval objv = stack_pop(jq); assert(jv_get_kind(k) == JV_KIND_STRING); assert(jv_get_kind(objv.value) == JV_KIND_OBJECT); objv.value = jv_object_set(objv.value, k, v); stack_push(jq, objv); stack_push(jq, stktop); break; } // FIXME: loadv/storev may do too much copying/freeing case LOADV: { uint16_t level = *pc++; uint16_t v = *pc++; frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level); jv* var = frame_local_var(fp, v); #if JQ_DEBUG printf("V%d = ", v); jv_dump(jv_copy(*var), 0); printf("\n"); #endif stack_push(jq, stackval_replace(stack_pop(jq), jv_copy(*var))); break; } case STOREV: { uint16_t level = *pc++; uint16_t v = *pc++; frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level); jv* var = frame_local_var(fp, v); stackval val = stack_pop(jq); #if JQ_DEBUG printf("V%d = ", v); jv_dump(jv_copy(val.value), 0); printf("\n"); #endif jv_free(*var); *var = val.value; break; } case GETPATH: { stackval path_end = stack_pop(jq); stackval path_start = stack_pop(jq); jv_free(path_end.value); jv path = jv_array(); for (int i=path_start.pathidx; ipathbuf[i])); } stack_push(jq, stackval_replace(path_start, path)); break; } case INDEX: { stackval t = stack_pop(jq); jv k = stack_pop(jq).value; int pathidx = path_push(jq, t, jv_copy(k)); jv v = jv_get(t.value, k); if (jv_is_valid(v)) { stackval sv; sv.value = v; sv.pathidx = pathidx; stack_push(jq, sv); } else { print_error(v); goto do_backtrack; } break; } case JUMP: { uint16_t offset = *pc++; pc += offset; break; } case JUMP_F: { uint16_t offset = *pc++; stackval t = stack_pop(jq); jv_kind kind = jv_get_kind(t.value); if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) { pc += offset; } stack_push(jq, t); // FIXME do this better break; } case EACH: stack_push(jq, stackval_root(jv_number(-1))); // fallthrough case ON_BACKTRACK(EACH): { int idx = jv_number_value(stack_pop(jq).value); stackval container = stack_pop(jq); int keep_going; jv key, value; if (jv_get_kind(container.value) == JV_KIND_ARRAY) { if (opcode == EACH) idx = 0; else idx = idx + 1; keep_going = idx < jv_array_length(jv_copy(container.value)); if (keep_going) { key = jv_number(idx); value = jv_array_get(jv_copy(container.value), idx); } } else if (jv_get_kind(container.value) == JV_KIND_OBJECT) { if (opcode == EACH) idx = jv_object_iter(container.value); else idx = jv_object_iter_next(container.value, idx); keep_going = jv_object_iter_valid(container.value, idx); if (keep_going) { key = jv_object_iter_key(container.value, idx); value = jv_object_iter_value(container.value, idx); } } else { assert(opcode == EACH); print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s", jv_kind_name(jv_get_kind(container.value))))); keep_going = 0; } if (!keep_going) { jv_free(container.value); goto do_backtrack; } else { stack_save(jq); stack_push(jq, container); stack_push(jq, stackval_root(jv_number(idx))); frame_push_backtrack(&jq->frame_stk, pc - 1); stack_switch(jq); stackval sv = {value, path_push(jq, container, key)}; stack_push(jq, sv); } break; } do_backtrack: case BACKTRACK: { if (!stack_restore(jq)) { return jv_invalid(); } pc = *frame_current_retaddr(&jq->frame_stk); frame_pop(&jq->frame_stk); backtracking = 1; break; } case FORK: { stack_save(jq); frame_push_backtrack(&jq->frame_stk, pc - 1); stack_switch(jq); pc++; // skip offset this time break; } case ON_BACKTRACK(FORK): { uint16_t offset = *pc++; pc += offset; break; } case YIELD: { jv value = stack_pop(jq).value; frame_push_backtrack(&jq->frame_stk, pc); return value; } case CALL_BUILTIN: { int nargs = *pc++; stackval top = stack_pop(jq); cfunc_input[0] = top.value; for (int i = 1; i < nargs; i++) { cfunc_input[i] = stack_pop(jq).value; } struct cfunction* func = &frame_current_bytecode(&jq->frame_stk)->globals->cfunctions[*pc++]; top.value = cfunction_invoke(func, cfunc_input); if (jv_is_valid(top.value)) { stack_push(jq, top); } else { print_error(top.value); goto do_backtrack; } break; } case CALL_JQ: { uint16_t nclosures = *pc++; uint16_t* retaddr = pc + 2 + nclosures*2; frame_ptr new_frame = frame_push(&jq->frame_stk, make_closure(&jq->frame_stk, frame_current(&jq->frame_stk), pc), retaddr); pc += 2; frame_ptr old_frame = forkable_stack_peek_next(&jq->frame_stk, new_frame); assert(nclosures == frame_self(new_frame)->bc->nclosures); for (int i=0; iframe_stk, old_frame, pc); pc += 2; } pc = frame_current_bytecode(&jq->frame_stk)->code; break; } case RET: { pc = *frame_current_retaddr(&jq->frame_stk); frame_pop(&jq->frame_stk); break; } } } } void jq_init(struct bytecode* bc, jv input, jq_state **jq) { jq_state *new_jq; new_jq = jv_mem_alloc(sizeof(*new_jq)); memset(new_jq, 0, sizeof(*new_jq)); forkable_stack_init(&new_jq->data_stk, sizeof(stackval) * 100); forkable_stack_init(&new_jq->frame_stk, 1024); forkable_stack_init(&new_jq->fork_stk, 1024); stack_push(new_jq, stackval_root(input)); struct closure top = {bc, -1}; frame_push(&new_jq->frame_stk, top, 0); frame_push_backtrack(&new_jq->frame_stk, bc->code); *jq = new_jq; } void jq_teardown(jq_state **jq) { jq_state *old_jq = *jq; if (old_jq == NULL) return; *jq = NULL; while (stack_restore(old_jq)) {} assert(forkable_stack_empty(&old_jq->fork_stk)); assert(forkable_stack_empty(&old_jq->data_stk)); assert(forkable_stack_empty(&old_jq->frame_stk)); forkable_stack_free(&old_jq->fork_stk); forkable_stack_free(&old_jq->data_stk); forkable_stack_free(&old_jq->frame_stk); for (int i=0; ipathsize; i++) { jv_free(old_jq->pathbuf[i]); } jv_mem_free(old_jq->pathbuf); jv_mem_free(old_jq); } struct bytecode* jq_compile(const char* str) { struct locfile locations; locfile_init(&locations, str, strlen(str)); block program; struct bytecode* bc = 0; int nerrors = jq_parse(&locations, &program); if (nerrors == 0) { program = builtins_bind(program); nerrors = block_compile(program, &locations, &bc); } if (nerrors) { fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error"); } locfile_free(&locations); return bc; }