bool ast_passes_type(ast_t** astp, pass_opt_t* options) { ast_t* ast = *astp; assert(ast_id(ast) == TK_ACTOR || ast_id(ast) == TK_CLASS || ast_id(ast) == TK_STRUCT || ast_id(ast) == TK_PRIMITIVE || ast_id(ast) == TK_TRAIT || ast_id(ast) == TK_INTERFACE); // We don't have the right frame stack for an entity, set up appropriate // frames ast_t* module = ast_parent(ast); ast_t* package = ast_parent(module); frame_push(&options->check, NULL); frame_push(&options->check, package); frame_push(&options->check, module); bool ok = ast_passes(astp, options, options->program_pass); frame_pop(&options->check); frame_pop(&options->check); frame_pop(&options->check); return ok; }
void pass_opt_init(pass_opt_t* options) { // Start with an empty typechecker frame. memset(options, 0, sizeof(pass_opt_t)); options->limit = PASS_ALL; frame_push(&options->check, NULL); }
void pass_opt_init(pass_opt_t* options) { // Start with an empty typechecker frame. memset(options, 0, sizeof(pass_opt_t)); options->limit = PASS_ALL; options->verbosity = VERBOSITY_INFO; options->check.errors = errors_alloc(); frame_push(&options->check, NULL); }
bool type_passes(ast_t* type, pass_opt_t* options) { ast_t* module = ast_parent(type); ast_t* package = ast_parent(module); frame_push(&options->check, NULL); frame_push(&options->check, package); frame_push(&options->check, module); bool ok = package_passes(type, options); if(ok) ok = program_passes(type, options); frame_pop(&options->check); frame_pop(&options->check); frame_pop(&options->check); return ok; }
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { typecheck_t* t = &options->check; ast_t* module = ast_nearest(*ast, TK_MODULE); ast_t* package = ast_parent(module); assert(module != NULL); assert(package != NULL); frame_push(t, NULL); frame_push(t, package); frame_push(t, module); ast_result_t ret = ast_visit(ast, pre, post, options, pass); frame_pop(t); frame_pop(t); frame_pop(t); return ret; }
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { typecheck_t* t = &options->check; bool pop = frame_push(t, NULL); ast_result_t ret = ast_visit(ast, pre, post, options, pass); if(pop) frame_pop(t); return ret; }
void jq_start(jq_state *jq, jv input, int flags) { jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data); jq_reset(jq); struct closure top = {jq->bc, -1}; struct frame* top_frame = frame_push(jq, top, 0, 0); top_frame->retdata = 0; top_frame->retaddr = 0; stack_push(jq, input); stack_save(jq, jq->bc->code, stack_get_pos(jq)); if (flags & JQ_DEBUG_TRACE) { jq->debug_trace_enabled = 1; } else { jq->debug_trace_enabled = 0; } jq->initial_execution = 1; }
void jq_init(struct bytecode* bc, jv input, jq_state **jq, int flags) { jq_state *new_jq; new_jq = jv_mem_alloc(sizeof(*new_jq)); memset(new_jq, 0, sizeof(*new_jq)); new_jq->path = jv_null(); forkable_stack_init(&new_jq->data_stk, sizeof(data_stk_elem) * 100); forkable_stack_init(&new_jq->frame_stk, 1024); forkable_stack_init(&new_jq->fork_stk, 1024); stack_push(new_jq, input); struct closure top = {bc, -1}; frame_push(&new_jq->frame_stk, top, 0); stack_save(new_jq, bc->code); stack_switch(new_jq); if (flags & JQ_DEBUG_TRACE) { new_jq->debug_trace_enabled = 1; } else { new_jq->debug_trace_enabled = 0; } new_jq->initial_execution = 1; *jq = new_jq; }
jv jq_next(jq_state *jq) { jv cfunc_input[MAX_CFUNCTION_ARGS]; jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data); uint16_t* pc = stack_restore(jq); assert(pc); int backtracking = !jq->initial_execution; jq->initial_execution = 0; while (1) { uint16_t opcode = *pc; if (jq->debug_trace_enabled) { dump_operation(frame_current(jq)->bc, pc); printf("\t"); const struct opcode_description* opdesc = opcode_describe(opcode); stack_ptr param = 0; if (!backtracking) { int stack_in = opdesc->stack_in; if (stack_in == -1) stack_in = pc[1]; int i; for (i=0; i<stack_in; i++) { if (i == 0) { param = jq->stk_top; } else { printf(" | "); param = *stack_block_next(&jq->stk, param); } if (!param) break; jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), 0); //printf("<%d>", jv_get_refcnt(param->val)); //printf(" -- "); //jv_dump(jv_copy(jq->path), 0); } } else { printf("\t<backtracking>"); } printf("\n"); } 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(jq)->bc->constants), *pc++); assert(jv_is_valid(v)); jv_free(stack_pop(jq)); stack_push(jq, v); break; } case DUP: { jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, v); break; } case DUP2: { jv keep = stack_pop(jq); jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, keep); stack_push(jq, v); break; } case SUBEXP_BEGIN: { jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, v); jq->subexp_nest++; break; } case SUBEXP_END: { assert(jq->subexp_nest > 0); jq->subexp_nest--; jv a = stack_pop(jq); jv b = stack_pop(jq); stack_push(jq, a); stack_push(jq, b); break; } case POP: { jv_free(stack_pop(jq)); break; } case APPEND: { jv v = stack_pop(jq); uint16_t level = *pc++; uint16_t vidx = *pc++; jv* var = frame_local_var(jq, vidx, level); assert(jv_get_kind(*var) == JV_KIND_ARRAY); *var = jv_array_append(*var, v); break; } case INSERT: { jv stktop = stack_pop(jq); jv v = stack_pop(jq); jv k = stack_pop(jq); jv objv = stack_pop(jq); assert(jv_get_kind(objv) == JV_KIND_OBJECT); if (jv_get_kind(k) == JV_KIND_STRING) { stack_push(jq, jv_object_set(objv, k, v)); stack_push(jq, stktop); } else { print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key", jv_kind_name(jv_get_kind(k))))); jv_free(stktop); jv_free(v); jv_free(k); jv_free(objv); goto do_backtrack; } break; } case ON_BACKTRACK(RANGE): case RANGE: { uint16_t level = *pc++; uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); jv max = stack_pop(jq); if (jv_get_kind(*var) != JV_KIND_NUMBER || jv_get_kind(max) != JV_KIND_NUMBER) { print_error(jq, jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric"))); jv_free(max); goto do_backtrack; } else if (jv_number_value(jv_copy(*var)) >= jv_number_value(jv_copy(max))) { /* finished iterating */ goto do_backtrack; } else { jv curr = jv_copy(*var); *var = jv_number(jv_number_value(*var) + 1); struct stack_pos spos = stack_get_pos(jq); stack_push(jq, jv_copy(max)); stack_save(jq, pc - 3, spos); stack_push(jq, curr); } break; } // FIXME: loadv/storev may do too much copying/freeing case LOADV: { uint16_t level = *pc++; uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); if (jq->debug_trace_enabled) { printf("V%d = ", v); jv_dump(jv_copy(*var), 0); printf("\n"); } jv_free(stack_pop(jq)); stack_push(jq, jv_copy(*var)); break; } // Does a load but replaces the variable with null case LOADVN: { uint16_t level = *pc++; uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); if (jq->debug_trace_enabled) { printf("V%d = ", v); jv_dump(jv_copy(*var), 0); printf("\n"); } jv_free(stack_pop(jq)); stack_push(jq, *var); *var = jv_null(); break; } case STOREV: { uint16_t level = *pc++; uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); jv val = stack_pop(jq); if (jq->debug_trace_enabled) { printf("V%d = ", v); jv_dump(jv_copy(val), 0); printf("\n"); } jv_free(*var); *var = val; break; } case PATH_BEGIN: { jv v = stack_pop(jq); stack_push(jq, jq->path); stack_save(jq, pc - 1, stack_get_pos(jq)); stack_push(jq, jv_number(jq->subexp_nest)); stack_push(jq, v); jq->path = jv_array(); jq->subexp_nest = 0; break; } case PATH_END: { jv v = stack_pop(jq); jv_free(v); // discard value, only keep path int old_subexp_nest = (int)jv_number_value(stack_pop(jq)); jv path = jq->path; jq->path = stack_pop(jq); struct stack_pos spos = stack_get_pos(jq); stack_push(jq, jv_copy(path)); stack_save(jq, pc - 1, spos); stack_push(jq, path); jq->subexp_nest = old_subexp_nest; break; } case ON_BACKTRACK(PATH_BEGIN): case ON_BACKTRACK(PATH_END): { jv_free(jq->path); jq->path = stack_pop(jq); goto do_backtrack; } case INDEX: case INDEX_OPT: { jv t = stack_pop(jq); jv k = stack_pop(jq); path_append(jq, jv_copy(k)); jv v = jv_get(t, k); if (jv_is_valid(v)) { stack_push(jq, v); } else { if (opcode == INDEX) print_error(jq, v); else jv_free(v); goto do_backtrack; } break; } case JUMP: { uint16_t offset = *pc++; pc += offset; break; } case JUMP_F: { uint16_t offset = *pc++; jv t = stack_pop(jq); jv_kind kind = jv_get_kind(t); if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) { pc += offset; } stack_push(jq, t); // FIXME do this better break; } case EACH: case EACH_OPT: stack_push(jq, jv_number(-1)); // fallthrough case ON_BACKTRACK(EACH): case ON_BACKTRACK(EACH_OPT): { int idx = jv_number_value(stack_pop(jq)); jv container = stack_pop(jq); int keep_going, is_last = 0; jv key, value; if (jv_get_kind(container) == JV_KIND_ARRAY) { if (opcode == EACH || opcode == EACH_OPT) idx = 0; else idx = idx + 1; int len = jv_array_length(jv_copy(container)); keep_going = idx < len; is_last = idx == len - 1; if (keep_going) { key = jv_number(idx); value = jv_array_get(jv_copy(container), idx); } } else if (jv_get_kind(container) == JV_KIND_OBJECT) { if (opcode == EACH || opcode == EACH_OPT) idx = jv_object_iter(container); else idx = jv_object_iter_next(container, idx); keep_going = jv_object_iter_valid(container, idx); if (keep_going) { key = jv_object_iter_key(container, idx); value = jv_object_iter_value(container, idx); } } else { assert(opcode == EACH || opcode == EACH_OPT); if (opcode == EACH) { print_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s", jv_kind_name(jv_get_kind(container))))); } keep_going = 0; } if (!keep_going) { jv_free(container); goto do_backtrack; } else if (is_last) { // we don't need to make a backtrack point jv_free(container); path_append(jq, key); stack_push(jq, value); } else { struct stack_pos spos = stack_get_pos(jq); stack_push(jq, container); stack_push(jq, jv_number(idx)); stack_save(jq, pc - 1, spos); path_append(jq, key); stack_push(jq, value); } break; } do_backtrack: case BACKTRACK: { pc = stack_restore(jq); if (!pc) { return jv_invalid(); } backtracking = 1; break; } case FORK: { stack_save(jq, pc - 1, stack_get_pos(jq)); pc++; // skip offset this time break; } case ON_BACKTRACK(FORK): { uint16_t offset = *pc++; pc += offset; break; } case CALL_BUILTIN: { int nargs = *pc++; jv top = stack_pop(jq); jv* in = cfunc_input; int i; in[0] = top; for (i = 1; i < nargs; i++) { in[i] = stack_pop(jq); } struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++]; typedef jv (*func_1)(jv); typedef jv (*func_2)(jv,jv); typedef jv (*func_3)(jv,jv,jv); typedef jv (*func_4)(jv,jv,jv,jv); typedef jv (*func_5)(jv,jv,jv,jv,jv); switch (function->nargs) { case 1: top = ((func_1)function->fptr)(in[0]); break; case 2: top = ((func_2)function->fptr)(in[0], in[1]); break; case 3: top = ((func_3)function->fptr)(in[0], in[1], in[2]); break; case 4: top = ((func_4)function->fptr)(in[0], in[1], in[2], in[3]); break; case 5: top = ((func_5)function->fptr)(in[0], in[1], in[2], in[3], in[4]); break; default: return jv_invalid_with_msg(jv_string("Function takes too many arguments")); } if (jv_is_valid(top)) { stack_push(jq, top); } else { print_error(jq, top); goto do_backtrack; } break; } case CALL_JQ: { jv input = stack_pop(jq); uint16_t nclosures = *pc++; uint16_t* retaddr = pc + 2 + nclosures*2; struct frame* new_frame = frame_push(jq, make_closure(jq, pc), pc + 2, nclosures); new_frame->retdata = jq->stk_top; new_frame->retaddr = retaddr; pc = new_frame->bc->code; stack_push(jq, input); break; } case RET: { jv value = stack_pop(jq); assert(jq->stk_top == frame_current(jq)->retdata); uint16_t* retaddr = frame_current(jq)->retaddr; if (retaddr) { // function return pc = retaddr; frame_pop(jq); } else { // top-level return, yielding value struct stack_pos spos = stack_get_pos(jq); stack_push(jq, jv_null()); stack_save(jq, pc - 1, spos); return value; } stack_push(jq, value); break; } case ON_BACKTRACK(RET): { // resumed after top-level return goto do_backtrack; } } } }
ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post, pass_opt_t* options, pass_id pass) { assert(ast != NULL); assert(*ast != NULL); pass_id ast_pass = (pass_id)ast_checkflag(*ast, AST_FLAG_PASS_MASK); if(ast_pass >= pass) // This pass already done for this AST node return AST_OK; if(ast_checkflag(*ast, AST_FLAG_PRESERVE)) // Do not process this subtree return AST_OK; typecheck_t* t = &options->check; bool pop = frame_push(t, *ast); ast_result_t ret = AST_OK; bool ignore = false; if(pre != NULL) { switch(pre(ast, options)) { case AST_OK: break; case AST_IGNORE: ignore = true; break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } } if(!ignore && ((pre != NULL) || (post != NULL))) { ast_t* child = ast_child(*ast); while(child != NULL) { switch(ast_visit(&child, pre, post, options, pass)) { case AST_OK: break; case AST_IGNORE: // Can never happen assert(0); break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } child = ast_sibling(child); } } if(!ignore && post != NULL) { switch(post(ast, options)) { case AST_OK: case AST_IGNORE: break; case AST_ERROR: ret = AST_ERROR; break; case AST_FATAL: record_ast_pass(*ast, pass); return AST_FATAL; } } if(pop) frame_pop(t); record_ast_pass(*ast, pass); return ret; }
void environ_pushframe(node_t *env_handle) { frame_push(env_handle, NULL, NULL, 0); }
jv jq_next(jq_state *jq) { jv cfunc_input[MAX_CFUNCTION_ARGS]; uint16_t* pc = stack_restore(jq); assert(pc); int backtracking = !jq->initial_execution; jq->initial_execution = 0; while (1) { uint16_t opcode = *pc; if (jq->debug_trace_enabled) { dump_operation(frame_current_bytecode(&jq->frame_stk), pc); printf("\t"); const struct opcode_description* opdesc = opcode_describe(opcode); data_stk_elem* param = 0; int stack_in = opdesc->stack_in; if (stack_in == -1) stack_in = pc[1]; for (int i=0; i<stack_in; i++) { if (i == 0) { param = forkable_stack_peek(&jq->data_stk); } else { printf(" | "); param = forkable_stack_peek_next(&jq->data_stk, param); } if (!param) break; jv_dump(jv_copy(param->val), 0); //printf("<%d>", jv_get_refcnt(param->val)); //printf(" -- "); //jv_dump(jv_copy(jq->path), 0); } if (backtracking) printf("\t<backtracking>"); printf("\n"); } 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)); jv_free(stack_pop(jq)); stack_push(jq, v); break; } case DUP: { jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, v); break; } case DUP2: { jv keep = stack_pop(jq); jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, keep); stack_push(jq, v); break; } case SUBEXP_BEGIN: { jv v = stack_pop(jq); stack_push(jq, jv_copy(v)); stack_push(jq, v); jq->subexp_nest++; break; } case SUBEXP_END: { assert(jq->subexp_nest > 0); jq->subexp_nest--; jv a = stack_pop(jq); jv b = stack_pop(jq); stack_push(jq, a); stack_push(jq, b); break; } case POP: { jv_free(stack_pop(jq)); break; } case APPEND: { jv v = stack_pop(jq); 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: { jv stktop = stack_pop(jq); jv v = stack_pop(jq); jv k = stack_pop(jq); jv objv = stack_pop(jq); assert(jv_get_kind(objv) == JV_KIND_OBJECT); if (jv_get_kind(k) == JV_KIND_STRING) { stack_push(jq, jv_object_set(objv, k, v)); stack_push(jq, stktop); } else { print_error(jv_invalid_with_msg(jv_string_fmt("Cannot use %s as object key", jv_kind_name(jv_get_kind(k))))); jv_free(stktop); jv_free(v); jv_free(k); jv_free(objv); goto do_backtrack; } break; } case ON_BACKTRACK(RANGE): case RANGE: { 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); jv max = stack_pop(jq); if (jv_get_kind(*var) != JV_KIND_NUMBER || jv_get_kind(max) != JV_KIND_NUMBER) { print_error(jv_invalid_with_msg(jv_string_fmt("Range bounds must be numeric"))); jv_free(max); goto do_backtrack; } else if (jv_number_value(jv_copy(*var)) >= jv_number_value(jv_copy(max))) { /* finished iterating */ goto do_backtrack; } else { jv curr = jv_copy(*var); *var = jv_number(jv_number_value(*var) + 1); stack_save(jq, pc - 3); stack_push(jq, jv_copy(max)); stack_switch(jq); stack_push(jq, curr); } 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_trace_enabled) { printf("V%d = ", v); jv_dump(jv_copy(*var), 0); printf("\n"); } jv_free(stack_pop(jq)); stack_push(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); jv val = stack_pop(jq); if (jq->debug_trace_enabled) { printf("V%d = ", v); jv_dump(jv_copy(val), 0); printf("\n"); } jv_free(*var); *var = val; break; } case PATH_BEGIN: { jv v = stack_pop(jq); stack_push(jq, jq->path); stack_save(jq, pc - 1); stack_switch(jq); stack_push(jq, jv_number(jq->subexp_nest)); stack_push(jq, v); jq->path = jv_array(); jq->subexp_nest = 0; break; } case PATH_END: { jv v = stack_pop(jq); jv_free(v); // discard value, only keep path int old_subexp_nest = (int)jv_number_value(stack_pop(jq)); jv path = jq->path; jq->path = stack_pop(jq); stack_save(jq, pc - 1); stack_push(jq, jv_copy(path)); stack_switch(jq); stack_push(jq, path); jq->subexp_nest = old_subexp_nest; break; } case ON_BACKTRACK(PATH_BEGIN): case ON_BACKTRACK(PATH_END): { jv_free(jq->path); jq->path = stack_pop(jq); goto do_backtrack; } case INDEX: { jv t = stack_pop(jq); jv k = stack_pop(jq); path_append(jq, jv_copy(k)); jv v = jv_get(t, k); if (jv_is_valid(v)) { stack_push(jq, v); } else { print_error(v); goto do_backtrack; } break; } case JUMP: { uint16_t offset = *pc++; pc += offset; break; } case JUMP_F: { uint16_t offset = *pc++; jv t = stack_pop(jq); jv_kind kind = jv_get_kind(t); 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, jv_number(-1)); // fallthrough case ON_BACKTRACK(EACH): { int idx = jv_number_value(stack_pop(jq)); jv container = stack_pop(jq); int keep_going; jv key, value; if (jv_get_kind(container) == JV_KIND_ARRAY) { if (opcode == EACH) idx = 0; else idx = idx + 1; keep_going = idx < jv_array_length(jv_copy(container)); if (keep_going) { key = jv_number(idx); value = jv_array_get(jv_copy(container), idx); } } else if (jv_get_kind(container) == JV_KIND_OBJECT) { if (opcode == EACH) idx = jv_object_iter(container); else idx = jv_object_iter_next(container, idx); keep_going = jv_object_iter_valid(container, idx); if (keep_going) { key = jv_object_iter_key(container, idx); value = jv_object_iter_value(container, 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))))); keep_going = 0; } if (!keep_going) { jv_free(container); goto do_backtrack; } else { stack_save(jq, pc - 1); stack_push(jq, container); stack_push(jq, jv_number(idx)); stack_switch(jq); path_append(jq, key); stack_push(jq, value); } break; } do_backtrack: case BACKTRACK: { pc = stack_restore(jq); if (!pc) { return jv_invalid(); } backtracking = 1; break; } case FORK: { stack_save(jq, pc - 1); stack_switch(jq); pc++; // skip offset this time break; } case ON_BACKTRACK(FORK): { uint16_t offset = *pc++; pc += offset; break; } case CALL_BUILTIN: { int nargs = *pc++; jv top = stack_pop(jq); cfunc_input[0] = top; for (int i = 1; i < nargs; i++) { cfunc_input[i] = stack_pop(jq); } struct cfunction* func = &frame_current_bytecode(&jq->frame_stk)->globals->cfunctions[*pc++]; top = cfunction_invoke(func, cfunc_input); if (jv_is_valid(top)) { stack_push(jq, top); } else { print_error(top); 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; i<nclosures; i++) { *frame_closure_arg(new_frame, i) = make_closure(&jq->frame_stk, old_frame, pc); pc += 2; } pc = frame_current_bytecode(&jq->frame_stk)->code; break; } case RET: { uint16_t* retaddr = *frame_current_retaddr(&jq->frame_stk); if (retaddr) { // function return pc = retaddr; frame_pop(&jq->frame_stk); } else { // top-level return, yielding value jv value = stack_pop(jq); stack_save(jq, pc - 1); stack_push(jq, jv_null()); stack_switch(jq); return value; } break; } case ON_BACKTRACK(RET): { // resumed after top-level return goto do_backtrack; } } } }