jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { // FIXME: don't do lookup for noninteger index v = jv_array_get(t, (int)jv_number_value(k)); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER)) { jv_free(t); jv_free(k); v = jv_null(); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); jv_free(t); jv_free(k); } return v; }
jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { if(jv_is_integer(k)){ v = jv_array_get(t, (int)jv_number_value(k)); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else { jv_free(t); jv_free(k); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_array_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_string_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_STRING) { v = jv_string_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) { v = jv_array_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER || jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); jv_free(t); jv_free(k); } return v; }
static pfunc check_literal(struct jv_parser* p) { if (p->tokenpos == 0) return 0; const char* pattern = 0; int plen; jv v; switch (p->tokenbuf[0]) { case 't': pattern = "true"; plen = 4; v = jv_true(); break; case 'f': pattern = "false"; plen = 5; v = jv_false(); break; case 'n': pattern = "null"; plen = 4; v = jv_null(); break; } if (pattern) { if (p->tokenpos != plen) return "Invalid literal"; for (int i=0; i<plen; i++) if (p->tokenbuf[i] != pattern[i]) return "Invalid literal"; TRY(value(p, v)); } else { // FIXME: better parser p->tokenbuf[p->tokenpos] = 0; // FIXME: invalid char* end = 0; double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end); if (end == 0 || *end != 0) return "Invalid numeric literal"; TRY(value(p, jv_number_s(p->tokenbuf, d))); } p->tokenpos = 0; return 0; }
static void f_add(jv input[], jv output[]) { jv array = input[0]; if (jv_get_kind(array) != JV_KIND_ARRAY) { output[0] = jv_invalid_with_msg(jv_string_fmt("Cannot add elements of an %s", jv_kind_name(jv_get_kind(array)))); } else if (jv_array_length(jv_copy(array)) == 0) { output[0] = jv_null(); } else { jv sum = jv_array_get(jv_copy(array), 0); for (int i = 1; i < jv_array_length(jv_copy(array)); i++) { jv x = jv_array_get(jv_copy(array), i); jv add_args[] = {jv_null(), x, sum}; f_plus(add_args, &sum); } output[0] = sum; } jv_free(array); }
jv jv_invalid_get_msg(jv inv) { assert(jv_get_kind(inv) == JV_KIND_INVALID); jv x; if (inv.u.ptr == 0) x = jv_null(); else x = jv_copy(((jvp_invalid*)inv.u.ptr)->errmsg); jv_free(inv); return x; }
static void jq_reset(jq_state *jq) { while (stack_restore(jq)) {} assert(jq->stk_top == 0); assert(jq->fork_top == 0); assert(jq->curr_frame == 0); stack_reset(&jq->stk); if (jv_get_kind(jq->path) != JV_KIND_INVALID) jv_free(jq->path); jq->path = jv_null(); jq->subexp_nest = 0; }
jq_state *jq_init(void) { jq_state *jq; jq = jv_mem_alloc_unguarded(sizeof(*jq)); if (jq == NULL) return NULL; jq->bc = 0; stack_init(&jq->stk); jq->stk_top = 0; jq->fork_top = 0; jq->curr_frame = 0; jq->err_cb = NULL; jq->err_cb_data = NULL; jq->path = jv_null(); return jq; }
int block_compile(block b, struct locfile* locations, struct bytecode** out) { struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode)); bc->parent = 0; bc->nclosures = 0; bc->globals = jv_mem_alloc(sizeof(struct symbol_table)); int ncfunc = count_cfunctions(b); bc->globals->ncfunctions = 0; bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc); bc->globals->cfunc_names = jv_array(); bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null()); int nerrors = compile(locations, bc, b); assert(bc->globals->ncfunctions == ncfunc); if (nerrors > 0) { bytecode_free(bc); *out = 0; } else { *out = bc; } return nerrors; }
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; }
int main(int argc, char* argv[]) { jq_state *jq = NULL; int ret = 0; int compiled = 0; char *t = NULL; if (argc) progname = argv[0]; if (argc > 1 && !strcmp(argv[1], "--run-tests")) { return jq_testsuite(argc, argv); } jq = jq_init(); if (jq == NULL) { perror("malloc"); ret = 2; goto out; } const char* program = 0; input_filenames = jv_mem_alloc(sizeof(const char*) * argc); ninput_files = 0; int further_args_are_files = 0; int jq_flags = 0; size_t short_opts = 0; jv program_arguments = jv_array(); jv lib_search_paths = jv_array(); for (int i=1; i<argc; i++, short_opts = 0) { if (further_args_are_files) { input_filenames[ninput_files++] = argv[i]; } else if (!strcmp(argv[i], "--")) { if (!program) usage(2); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { input_filenames[ninput_files++] = argv[i]; } else { program = argv[i]; } } else { if (argv[i][1] == 'L') { if (argv[i][2] != 0) { // -Lname (faster check than strlen) lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i]+2)); } else if (i >= argc - 1) { fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n"); die(); } else { lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i+1])); i++; } continue; } if (isoption(argv[i], 's', "slurp", &short_opts)) { options |= SLURP; if (!short_opts) continue; } if (isoption(argv[i], 'r', "raw-output", &short_opts)) { options |= RAW_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'c', "compact-output", &short_opts)) { options |= COMPACT_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'C', "color-output", &short_opts)) { options |= COLOUR_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) { options |= NO_COLOUR_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'a', "ascii-output", &short_opts)) { options |= ASCII_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 0, "unbuffered", &short_opts)) { options |= UNBUFFERED_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'S', "sort-keys", &short_opts)) { options |= SORTED_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'R', "raw-input", &short_opts)) { options |= RAW_INPUT; if (!short_opts) continue; } if (isoption(argv[i], 'n', "null-input", &short_opts)) { options |= PROVIDE_NULL; if (!short_opts) continue; } if (isoption(argv[i], 'f', "from-file", &short_opts)) { options |= FROM_FILE; if (!short_opts) continue; } if (isoption(argv[i], 'j', "join-output", &short_opts)) { options |= RAW_OUTPUT | RAW_NO_LF; if (!short_opts) continue; } if (isoption(argv[i], 'i', "in-place", &short_opts)) { options |= IN_PLACE; if (!short_opts) continue; } if (isoption(argv[i], 'e', "exit-status", &short_opts)) { options |= EXIT_STATUS; if (!short_opts) continue; } if (isoption(argv[i], 0, "arg", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2])); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments if (!short_opts) continue; } if (isoption(argv[i], 0, "argfile", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); jv data = jv_load_file(argv[i+2], 0); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: Bad JSON in --argfile %s %s: %s\n", progname, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); ret = 2; goto out; } if (jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1) data = jv_array_get(data, 0); arg = jv_object_set(arg, jv_string("value"), data); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments if (!short_opts) continue; } if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) { options |= DUMP_DISASM; if (!short_opts) continue; } if (isoption(argv[i], 0, "debug-trace", &short_opts)) { jq_flags |= JQ_DEBUG_TRACE; if (!short_opts) continue; } if (isoption(argv[i], 'h', "help", &short_opts)) { usage(0); if (!short_opts) continue; } if (isoption(argv[i], 'V', "version", &short_opts)) { printf("jq-%s\n", JQ_VERSION); ret = 0; goto out; } // check for unknown options... if this argument was a short option if (strlen(argv[i]) != short_opts + 1) { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } } char *penv = getenv("JQ_LIBRARY_PATH"); if (penv) { #ifdef WIN32 #define PATH_ENV_SEPARATOR ";" #else #define PATH_ENV_SEPARATOR ":" #endif lib_search_paths = jv_array_concat(lib_search_paths,jv_string_split(jv_string(penv),jv_string(PATH_ENV_SEPARATOR))); #undef PATH_ENV_SEPARATOR } jq_set_lib_dirs(jq,lib_search_paths); char *origin = strdup(argv[0]); if (origin == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(1); } jq_set_lib_origin(jq,jv_string(dirname(origin))); free(origin); #if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY) #if defined(HAVE__ISATTY) && defined(isatty) #undef isatty #define isatty _isatty #endif if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO)) program = "."; #endif if (!program) usage(2); if ((options & IN_PLACE)) { if (ninput_files == 0) usage(2); if (strcmp(input_filenames[0], "-") == 0) usage(2); size_t tlen = strlen(input_filenames[0]) + 7; t = jv_mem_alloc(tlen); int n = snprintf(t, tlen,"%sXXXXXX", input_filenames[0]); assert(n > 0 && (size_t)n < tlen); if (mkstemp(t) == -1) { fprintf(stderr, "Error: %s creating temporary file", strerror(errno)); exit(3); } if (freopen(t, "w", stdout) == NULL) { fprintf(stderr, "Error: %s redirecting stdout to temporary file", strerror(errno)); exit(3); } } if (ninput_files == 0) current_input = stdin; if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname); die(); } if (options & FROM_FILE) { jv data = jv_load_file(program, 1); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: %s\n", progname, jv_string_value(data)); jv_free(data); ret = 2; goto out; } compiled = jq_compile_args(jq, jv_string_value(data), program_arguments); jv_free(data); } else { compiled = jq_compile_args(jq, program, program_arguments); } if (!compiled){ ret = 3; goto out; } if (options & DUMP_DISASM) { jq_dump_disassembly(jq, 0); printf("\n"); } if (options & PROVIDE_NULL) { ret = process(jq, jv_null(), jq_flags); } else { jv slurped; if (options & SLURP) { if (options & RAW_INPUT) { slurped = jv_string(""); } else { slurped = jv_array(); } } struct jv_parser* parser = jv_parser_new(0); char buf[4096]; while (read_more(buf, sizeof(buf))) { if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; ret = process(jq, jv_string(buf), jq_flags); } } } else { jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(parser)))) { if (options & SLURP) { slurped = jv_array_append(slurped, value); } else { ret = process(jq, value, jq_flags); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); ret = 4; break; } else { jv_free(value); } } } jv_parser_free(parser); if (ret != 0) goto out; if (options & SLURP) { ret = process(jq, slurped, jq_flags); } } if ((options & IN_PLACE)) { #ifdef WIN32 (void) freopen("NUL", "w+", stdout); #else (void) freopen("/dev/null", "w+", stdout); #endif if (rename(t, input_filenames[0]) == -1) { fprintf(stderr, "Error: %s renaming temporary file", strerror(errno)); exit(3); } jv_mem_free(t); } out: jv_mem_free(input_filenames); jq_teardown(&jq); if (ret >= 10 && (options & EXIT_STATUS)) return ret - 10; if (ret >= 10) return 0; return ret; }
jv jv_invalid() { return jv_invalid_with_msg(jv_null()); }
int main(int argc, char* argv[]) { jq_state *jq = NULL; int ret = 0; int compiled = 0; int parser_flags = 0; int nfiles = 0; int badwrite; jv program_arguments = jv_array(); #ifdef WIN32 SetConsoleOutputCP(CP_UTF8); fflush(stdout); fflush(stderr); _setmode(fileno(stdout), _O_TEXT | _O_U8TEXT); _setmode(fileno(stderr), _O_TEXT | _O_U8TEXT); int wargc; wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &wargc); assert(wargc == argc); size_t arg_sz; for (int i = 0; i < argc; i++) { argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, 0, 0, 0, 0))); WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0); } #endif if (argc) progname = argv[0]; jq = jq_init(); if (jq == NULL) { perror("malloc"); ret = 2; goto out; } int dumpopts = JV_PRINT_INDENT_FLAGS(2); const char* program = 0; jv_extra_opt extra_opt; extra_opt.array_fold=0; extra_opt.array_fold_indent=1; jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb int further_args_are_files = 0; int jq_flags = 0; size_t short_opts = 0; jv lib_search_paths = jv_null(); for (int i=1; i<argc; i++, short_opts = 0) { if (further_args_are_files) { jq_util_input_add_input(input_state, argv[i]); nfiles++; } else if (!strcmp(argv[i], "--")) { if (!program) usage(2); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { jq_util_input_add_input(input_state, argv[i]); nfiles++; } else { program = argv[i]; } } else { if (argv[i][1] == 'L') { if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) lib_search_paths = jv_array(); if (argv[i][2] != 0) { // -Lname (faster check than strlen) lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i]+2))); } else if (i >= argc - 1) { fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n"); die(); } else { lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i+1]))); i++; } continue; } if (isoption(argv[i], 0, "fold", &short_opts)) { if (i >= argc - 1) { fprintf(stderr, "%s: --fold takes one parameter\n", progname); die(); } extra_opt.array_fold = atoi(argv[i+1]); if (extra_opt.array_fold < 1 || extra_opt.array_fold > 255) { fprintf(stderr, "%s: --fold takes a number between 1 and 255\n", progname); die(); } i++; if (!short_opts) continue; } if (isoption(argv[i], 's', "slurp", &short_opts)) { options |= SLURP; if (!short_opts) continue; } if (isoption(argv[i], 'r', "raw-output", &short_opts)) { options |= RAW_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'c', "compact-output", &short_opts)) { dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7)); if (!short_opts) continue; } if (isoption(argv[i], 'C', "color-output", &short_opts)) { options |= COLOR_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) { options |= NO_COLOR_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'a', "ascii-output", &short_opts)) { options |= ASCII_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 0, "unbuffered", &short_opts)) { options |= UNBUFFERED_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'S', "sort-keys", &short_opts)) { options |= SORTED_OUTPUT; if (!short_opts) continue; } if (isoption(argv[i], 'R', "raw-input", &short_opts)) { options |= RAW_INPUT; if (!short_opts) continue; } if (isoption(argv[i], 'n', "null-input", &short_opts)) { options |= PROVIDE_NULL; if (!short_opts) continue; } if (isoption(argv[i], 'f', "from-file", &short_opts)) { options |= FROM_FILE; if (!short_opts) continue; } if (isoption(argv[i], 'j', "join-output", &short_opts)) { options |= RAW_OUTPUT | RAW_NO_LF; if (!short_opts) continue; } if (isoption(argv[i], 0, "tab", &short_opts)) { dumpopts &= ~JV_PRINT_INDENT_FLAGS(7); dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY; if (!short_opts) continue; } if (isoption(argv[i], 0, "indent", &short_opts)) { if (i >= argc - 1) { fprintf(stderr, "%s: --indent takes one parameter\n", progname); die(); } dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7)); int indent = atoi(argv[i+1]); if (indent < -1 || indent > 7) { fprintf(stderr, "%s: --indent takes a number between -1 and 7\n", progname); die(); } dumpopts |= JV_PRINT_INDENT_FLAGS(indent); i++; if (!short_opts) continue; } if (isoption(argv[i], 0, "seq", &short_opts)) { options |= SEQ; if (!short_opts) continue; } if (isoption(argv[i], 0, "stream", &short_opts)) { parser_flags |= JV_PARSE_STREAMING; if (!short_opts) continue; } if (isoption(argv[i], 0, "stream-errors", &short_opts)) { parser_flags |= JV_PARSE_STREAM_ERRORS; if (!short_opts) continue; } if (isoption(argv[i], 'e', "exit-status", &short_opts)) { options |= EXIT_STATUS; if (!short_opts) continue; } // FIXME: For --arg* we should check that the varname is acceptable if (isoption(argv[i], 0, "arg", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2])); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments if (!short_opts) continue; } if (isoption(argv[i], 0, "argjson", &short_opts)) { if (i >= argc - 2) { fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname); die(); } jv v = jv_parse(argv[i+2]); if (!jv_is_valid(v)) { fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); arg = jv_object_set(arg, jv_string("value"), v); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments if (!short_opts) continue; } if (isoption(argv[i], 0, "argfile", &short_opts) || isoption(argv[i], 0, "slurpfile", &short_opts)) { const char *which; if (isoption(argv[i], 0, "argfile", &short_opts)) which = "argfile"; else which = "slurpfile"; if (i >= argc - 2) { fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); jv data = jv_load_file(argv[i+2], 0); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); jv_free(arg); ret = 2; goto out; } if (isoption(argv[i], 0, "argfile", &short_opts) && jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1) data = jv_array_get(data, 0); arg = jv_object_set(arg, jv_string("value"), data); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments if (!short_opts) continue; } if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) { options |= DUMP_DISASM; if (!short_opts) continue; } if (isoption(argv[i], 0, "debug-trace", &short_opts)) { jq_flags |= JQ_DEBUG_TRACE; if (!short_opts) continue; } if (isoption(argv[i], 'h', "help", &short_opts)) { usage(0); if (!short_opts) continue; } if (isoption(argv[i], 'V', "version", &short_opts)) { printf("jq-%s\n", JQ_VERSION); ret = 0; goto out; } if (isoption(argv[i], 0, "run-tests", &short_opts)) { i++; // XXX Pass program_arguments, even a whole jq_state *, through; // could be useful for testing ret = jq_testsuite(lib_search_paths, (options & DUMP_DISASM) || (jq_flags & JQ_DEBUG_TRACE), argc - i, argv + i); goto out; } // check for unknown options... if this argument was a short option if (strlen(argv[i]) != short_opts + 1) { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } } if (isatty(fileno(stdout))) { dumpopts |= JV_PRINT_ISATTY; #ifndef WIN32 /* Disable color by default on Windows builds as Windows terminals tend not to display it correctly */ dumpopts |= JV_PRINT_COLOR; #endif } if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR; if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR; if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) { // Default search path list lib_search_paths = JV_ARRAY(jv_string("~/.jq"), jv_string("$ORIGIN/../lib/jq"), jv_string("$ORIGIN/lib")); } jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_search_paths); char *origin = strdup(argv[0]); if (origin == NULL) { fprintf(stderr, "Error: out of memory\n"); exit(1); } jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string(dirname(origin))); free(origin); if (strchr(JQ_VERSION, '-') == NULL) jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION)); else jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string_fmt("%.*s-master", (int)(strchr(JQ_VERSION, '-') - JQ_VERSION), JQ_VERSION)); #if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY) #if defined(HAVE__ISATTY) && defined(isatty) #undef isatty #define isatty _isatty #endif if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO)) program = "."; #endif if (!program) usage(2); if (options & FROM_FILE) { char *program_origin = strdup(program); if (program_origin == NULL) { perror("malloc"); exit(2); } jv data = jv_load_file(program, 1); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: %s\n", progname, jv_string_value(data)); jv_free(data); ret = 2; goto out; } jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin)))); compiled = jq_compile_args(jq, jv_string_value(data), jv_copy(program_arguments)); free(program_origin); jv_free(data); } else { jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good? compiled = jq_compile_args(jq, program, jv_copy(program_arguments)); } if (!compiled){ ret = 3; goto out; } if (options & DUMP_DISASM) { jq_dump_disassembly(jq, 0); printf("\n"); } if ((options & SEQ)) parser_flags |= JV_PARSE_SEQ; if ((options & RAW_INPUT)) jq_util_input_set_parser(input_state, NULL, (options & SLURP) ? 1 : 0); else jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0); // Let jq program read from inputs jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state); // Let jq program call `debug` builtin and have that go somewhere jq_set_debug_cb(jq, debug_cb, &dumpopts); if (nfiles == 0) jq_util_input_add_input(input_state, "-"); if (options & PROVIDE_NULL) { if (extra_opt.array_fold==0) { ret = process(jq, jv_null(), jq_flags, dumpopts); } else { ret = process_extra_opt(jq, jv_null(), jq_flags, dumpopts, &extra_opt); } } else { jv value; while (jq_util_input_errors(input_state) == 0 && (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) { if (jv_is_valid(value)) { if (extra_opt.array_fold==0) { ret = process(jq, value, jq_flags, dumpopts); } else { ret = process_extra_opt(jq, value, jq_flags, dumpopts, &extra_opt); } continue; } // Parse error jv msg = jv_invalid_get_msg(value); if (!(options & SEQ)) { // --seq -> errors are not fatal ret = 4; fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); break; } fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg)); jv_free(msg); } } if (jq_util_input_errors(input_state) != 0) ret = 2; out: badwrite = ferror(stdout); if (fclose(stdout)!=0 || badwrite) { fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno)); ret = 2; } jv_free(program_arguments); jq_util_input_free(&input_state); jq_teardown(&jq); if (ret >= 10 && (options & EXIT_STATUS)) return ret - 10; if (ret >= 10) return 0; return ret; }
int main(int argc, char* argv[]) { if (argc) progname = argv[0]; const char* program = 0; for (int i=1; i<argc; i++) { if (!isoptish(argv[i])) { if (program) usage(); program = argv[i]; } else if (isoption(argv[i], 's', "slurp")) { options |= SLURP; } else if (isoption(argv[i], 'r', "raw-output")) { options |= RAW_OUTPUT; } else if (isoption(argv[i], 'c', "compact-output")) { options |= COMPACT_OUTPUT; } else if (isoption(argv[i], 'a', "ascii-output")) { options |= ASCII_OUTPUT; } else if (isoption(argv[i], 'R', "raw-input")) { options |= RAW_INPUT; } else if (isoption(argv[i], 'n', "null-input")) { options |= PROVIDE_NULL; } else if (isoption(argv[i], 'h', "help")) { usage(); } else { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } if (!program) usage(); if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", program); die(); } bc = jq_compile(program); if (!bc) return 1; #if JQ_DEBUG dump_disassembly(0, bc); printf("\n"); #endif if (options & PROVIDE_NULL) { process(jv_null()); } else { jv slurped; if (options & SLURP) slurped = jv_invalid(); int first = 1; struct jv_parser parser; jv_parser_init(&parser); while (!feof(stdin)) { char buf[4096]; if (!fgets(buf, sizeof(buf), stdin)) buf[0] = 0; if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { if (first) slurped = jv_string(buf); else slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; process(jv_string(buf)); } } } else { jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(&parser)))) { if (options & SLURP) { if (first) slurped = jv_array(); slurped = jv_array_append(slurped, value); } else { process(value); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); break; } else { jv_free(value); } } first = 0; } jv_parser_free(&parser); if (options & SLURP) { if (jv_is_valid(slurped)) { process(slurped); } else { jv_free(slurped); } } } bytecode_free(bc); return 0; }
int main(int argc, char* argv[]) { int ret = 0; if (argc) progname = argv[0]; if (argc > 1 && !strcmp(argv[1], "--run-tests")) { return jq_testsuite(argc - 1, argv + 1); } const char* program = 0; input_filenames = jv_mem_alloc(sizeof(const char*) * argc); ninput_files = 0; int further_args_are_files = 0; int jq_flags = 0; jv program_arguments = jv_array(); for (int i=1; i<argc; i++) { if (further_args_are_files) { input_filenames[ninput_files++] = argv[i]; } else if (!strcmp(argv[i], "--")) { if (!program) usage(); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { input_filenames[ninput_files++] = argv[i]; } else { program = argv[i]; } } else if (isoption(argv[i], 's', "slurp")) { options |= SLURP; } else if (isoption(argv[i], 'r', "raw-output")) { options |= RAW_OUTPUT; } else if (isoption(argv[i], 'c', "compact-output")) { options |= COMPACT_OUTPUT; } else if (isoption(argv[i], 'C', "color-output")) { options |= COLOUR_OUTPUT; } else if (isoption(argv[i], 'M', "monochrome-output")) { options |= NO_COLOUR_OUTPUT; } else if (isoption(argv[i], 'a', "ascii-output")) { options |= ASCII_OUTPUT; } else if (isoption(argv[i], 'R', "raw-input")) { options |= RAW_INPUT; } else if (isoption(argv[i], 'n', "null-input")) { options |= PROVIDE_NULL; } else if (isoption(argv[i], 'f', "from-file")) { options |= FROM_FILE; } else if (isoption(argv[i], 0, "arg")) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2])); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments } else if (isoption(argv[i], 0, "debug-dump-disasm")) { options |= DUMP_DISASM; } else if (isoption(argv[i], 0, "debug-trace")) { jq_flags |= JQ_DEBUG_TRACE; } else if (isoption(argv[i], 'h', "help")) { usage(); } else if (isoption(argv[i], 'V', "version")) { fprintf(stderr, "jq version %s\n", PACKAGE_VERSION); return 0; } else { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } if (!program) usage(); if (ninput_files == 0) current_input = stdin; if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname); die(); } if (options & FROM_FILE) { jv data = slurp_file(program); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: %s\n", progname, jv_string_value(data)); jv_free(data); return 1; } bc = jq_compile_args(jv_string_value(data), program_arguments); jv_free(data); } else { bc = jq_compile_args(program, program_arguments); } if (!bc) return 1; if (options & DUMP_DISASM) { dump_disassembly(0, bc); printf("\n"); } if (options & PROVIDE_NULL) { process(jv_null(), jq_flags); } else { jv slurped; if (options & SLURP) { if (options & RAW_INPUT) { slurped = jv_string(""); } else { slurped = jv_array(); } } struct jv_parser parser; jv_parser_init(&parser); char buf[4096]; while (read_more(buf, sizeof(buf))) { if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; process(jv_string(buf), jq_flags); } } } else { jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(&parser)))) { if (options & SLURP) { slurped = jv_array_append(slurped, value); } else { process(value, jq_flags); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); ret = 1; break; } else { jv_free(value); } } } jv_parser_free(&parser); if (ret != 0) goto out; if (options & SLURP) { process(slurped, jq_flags); } } out: jv_mem_free(input_filenames); bytecode_free(bc); return ret; }
int main(int argc, char* argv[]) { jq_state *jq = NULL; int ret = 0; int compiled = 0; if (argc) progname = argv[0]; if (argc > 1 && !strcmp(argv[1], "--run-tests")) { return jq_testsuite(argc, argv); } jq = jq_init(); if (jq == NULL) { perror("malloc"); ret = 1; goto out; } const char* program = 0; input_filenames = jv_mem_alloc(sizeof(const char*) * argc); ninput_files = 0; int further_args_are_files = 0; int jq_flags = 0; jv_parser_flags parser_flags = 0; jv program_arguments = jv_array(); for (int i=1; i<argc; i++) { if (further_args_are_files) { input_filenames[ninput_files++] = argv[i]; } else if (!strcmp(argv[i], "--")) { if (!program) usage(); further_args_are_files = 1; } else if (!isoptish(argv[i])) { if (program) { input_filenames[ninput_files++] = argv[i]; } else { program = argv[i]; } } else if (isoption(argv[i], 's', "slurp")) { options |= SLURP; } else if (isoption(argv[i], 'r', "raw-output")) { options |= RAW_OUTPUT; } else if (isoption(argv[i], 'c', "compact-output")) { options |= COMPACT_OUTPUT; } else if (isoption(argv[i], 'C', "color-output")) { options |= COLOUR_OUTPUT; } else if (isoption(argv[i], 'M', "monochrome-output")) { options |= NO_COLOUR_OUTPUT; } else if (isoption(argv[i], 'a', "ascii-output")) { options |= ASCII_OUTPUT; } else if (isoption(argv[i], 0, "unbuffered")) { options |= UNBUFFERED_OUTPUT; } else if (isoption(argv[i], 'S', "sort-keys")) { options |= SORTED_OUTPUT; } else if (isoption(argv[i], 'R', "raw-input")) { options |= RAW_INPUT; } else if (isoption(argv[i], 'n', "null-input")) { options |= PROVIDE_NULL; } else if (isoption(argv[i], 'f', "from-file")) { options |= FROM_FILE; } else if (isoption(argv[i], 'e', "exit-status")) { options |= EXIT_STATUS; } else if (isoption(argv[i], 'I', "online-input")) { parser_flags = JV_PARSE_EXPLODE_TOPLEVEL_ARRAY; } else if (isoption(argv[i], 0, "arg")) { if (i >= argc - 2) { fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2])); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments } else if (isoption(argv[i], 0, "argfile")) { if (i >= argc - 2) { fprintf(stderr, "%s: --argfile takes two parameters (e.g. -a varname filename)\n", progname); die(); } jv arg = jv_object(); arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1])); jv data = jv_load_file(argv[i+2], 0); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: Bad JSON in --argfile %s %s: %s\n", progname, argv[i+1], argv[i+2], jv_string_value(data)); jv_free(data); ret = 1; goto out; } if (jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1) data = jv_array_get(data, 0); arg = jv_object_set(arg, jv_string("value"), data); program_arguments = jv_array_append(program_arguments, arg); i += 2; // skip the next two arguments } else if (isoption(argv[i], 0, "debug-dump-disasm")) { options |= DUMP_DISASM; } else if (isoption(argv[i], 0, "debug-trace")) { jq_flags |= JQ_DEBUG_TRACE; } else if (isoption(argv[i], 'h', "help")) { usage(); } else if (isoption(argv[i], 'V', "version")) { printf("jq-%s\n", JQ_VERSION); ret = 0; goto out; } else { fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]); die(); } } if (!program) usage(); if (ninput_files == 0) current_input = stdin; if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) { fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname); die(); } if (options & FROM_FILE) { jv data = jv_load_file(program, 1); if (!jv_is_valid(data)) { data = jv_invalid_get_msg(data); fprintf(stderr, "%s: %s\n", progname, jv_string_value(data)); jv_free(data); ret = 1; goto out; } compiled = jq_compile_args(jq, jv_string_value(data), program_arguments); jv_free(data); } else { compiled = jq_compile_args(jq, program, program_arguments); } if (!compiled){ ret = 1; goto out; } if (options & DUMP_DISASM) { jq_dump_disassembly(jq, 0); printf("\n"); } if (options & PROVIDE_NULL) { ret = process(jq, jv_null(), jq_flags); } else { jv slurped; if (options & SLURP) { if (options & RAW_INPUT) { slurped = jv_string(""); } else { slurped = jv_array(); } } struct jv_parser* parser = jv_parser_new(parser_flags); char buf[4096]; while (read_more(buf, sizeof(buf))) { if (options & RAW_INPUT) { int len = strlen(buf); if (len > 0) { if (options & SLURP) { slurped = jv_string_concat(slurped, jv_string(buf)); } else { if (buf[len-1] == '\n') buf[len-1] = 0; ret = process(jq, jv_string(buf), jq_flags); } } } else { jv_parser_set_buf(parser, buf, strlen(buf), !feof(stdin)); jv value; while (jv_is_valid((value = jv_parser_next(parser)))) { if (options & SLURP) { slurped = jv_array_append(slurped, value); } else { ret = process(jq, value, jq_flags); } } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); fprintf(stderr, "parse error: %s\n", jv_string_value(msg)); jv_free(msg); ret = 1; break; } else { jv_free(value); } } } jv_parser_free(parser); if (ret != 0) goto out; if (options & SLURP) { ret = process(jq, slurped, jq_flags); } } out: jv_mem_free(input_filenames); jq_teardown(&jq); if (ret >= 10 && ret <= 11 && !(options & EXIT_STATUS)) return 0; return ret; }
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; } } } }
jv jv_get(jv t, jv k) { jv v; if (jv_get_kind(t) == JV_KIND_OBJECT && jv_get_kind(k) == JV_KIND_STRING) { v = jv_object_get(t, k); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) { if(jv_is_integer(k)){ int idx = (int)jv_number_value(k); if (idx < 0) idx += jv_array_length(jv_copy(t)); v = jv_array_get(t, idx); if (!jv_is_valid(v)) { jv_free(v); v = jv_null(); } } else { jv_free(t); jv_free(k); v = jv_null(); } } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_array_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, k, &start, &end)) { v = jv_string_slice(t, start, end); } else { v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers")); jv_free(t); } } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_STRING) { v = jv_string_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) { v = jv_array_indexes(t, k); } else if (jv_get_kind(t) == JV_KIND_NULL && (jv_get_kind(k) == JV_KIND_STRING || jv_get_kind(k) == JV_KIND_NUMBER || jv_get_kind(k) == JV_KIND_OBJECT)) { jv_free(t); jv_free(k); v = jv_null(); } else { /* * If k is a short string it's probably from a jq .foo expression or * similar, in which case putting it in the invalid msg may help the * user. The length 30 is arbitrary. */ if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"", jv_kind_name(jv_get_kind(t)), jv_string_value(k))); } else { v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s", jv_kind_name(jv_get_kind(t)), jv_kind_name(jv_get_kind(k)))); } jv_free(t); jv_free(k); } return v; }
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; } } } }