void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) { va_list fmtargs; va_start(fmtargs, fmt); int startline; int offset; if (loc.start != -1) { startline = locfile_get_line(l, loc.start); offset = l->linemap[startline]; } jv m1 = jv_string_vfmt(fmt, fmtargs); if (!jv_is_valid(m1)) { jq_report_error(l->jq, m1); return; } if (loc.start == -1) { jq_report_error(l->jq, jv_string_fmt("jq: error: %s\n<unknown location>", jv_string_value(m1))); jv_free(m1); return; } jv m2 = jv_string_fmt("%s\n%.*s%*s", jv_string_value(m1), locfile_line_length(l, startline), l->data + offset, loc.start - offset, ""); jv_free(m1); jq_report_error(l->jq, m2); return; }
static int string_cmp(const void* pa, const void* pb){ const jv* a = pa; const jv* b = pb; int lena = jv_string_length(jv_copy(*a)); int lenb = jv_string_length(jv_copy(*b)); int minlen = lena < lenb ? lena : lenb; int r = memcmp(jv_string_value(*a), jv_string_value(*b), minlen); if (r == 0) r = lena - lenb; return r; }
static void test_err_cb(void *data, jv e) { struct err_data *err_data = data; if (jv_get_kind(e) != JV_KIND_STRING) e = jv_dump_string(e, JV_PRINT_INVALID); if (!strncmp(jv_string_value(e), "jq: error", sizeof("jq: error") - 1)) snprintf(err_data->buf, sizeof(err_data->buf), "%s", jv_string_value(e)); if (strchr(err_data->buf, '\n')) *(strchr(err_data->buf, '\n')) = '\0'; jv_free(e); }
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) { jq_err_cb cb; void *cb_data; va_list fmtargs; va_start(fmtargs, fmt); int startline; int offset; if (loc.start != -1) { startline = locfile_get_line(l, loc.start); offset = l->linemap[startline]; } jq_get_error_cb(l->jq, &cb, &cb_data); jv m1 = jv_string_vfmt(fmt, fmtargs); if (!jv_is_valid(m1)) { jv_free(m1); goto enomem; } jv m2; if (loc.start == -1) { m2 = jv_string_fmt("%s\n<unknown location>", jv_string_value(m1)); if (cb) cb(cb_data, m2); else fprintf(stderr, "%s", jv_string_value(m2)); jv_free(m1); jv_free(m2); return; } m2 = jv_string_fmt("%s\n%.*s%*s", jv_string_value(m1), locfile_line_length(l, startline), l->data + offset, loc.start - offset, ""); jv_free(m1); if (!jv_is_valid(m2)) { jv_free(m2); goto enomem; } if (cb) cb(cb_data, m2); else fprintf(stderr, "%s", jv_string_value(m2)); jv_free(m2); return; enomem: if (cb != NULL) cb(cb_data, jv_invalid()); else if (errno == ENOMEM || errno == 0) fprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno ? errno : ENOMEM)); else fprintf(stderr, "Error formatting jq compilation error: %s", strerror(errno)); return; }
static void php_jq_filter(zval **return_value, jq_state *jq, jv json, int flags TSRMLS_DC) { jv result; jq_start(jq, jv_copy(json), 0); if (jv_is_valid(result = jq_next(jq))) { int multiple = 0; while (1) { zval *zv; ALLOC_INIT_ZVAL(zv); if (flags == JQ_OPT_RAW) { if (jv_get_kind(result) == JV_KIND_STRING) { ZVAL_STRING(zv, jv_string_value(result), 1); } else { jv dump = jv_dump_string(result, 0); if (jv_is_valid(dump)) { ZVAL_STRING(zv, jv_string_value(dump), 1); } jv_free(dump); } } else { php_jv_dump(&zv, result TSRMLS_CC); } if (!jv_is_valid(result = jq_next(jq))) { if (multiple) { zend_hash_next_index_insert(Z_ARRVAL_PP(return_value), &zv, sizeof(zv), NULL); } else { ZVAL_ZVAL(*return_value, zv, 1, 1); } break; } if (!multiple) { multiple = 1; array_init(*return_value); } zend_hash_next_index_insert(Z_ARRVAL_PP(return_value), &zv, sizeof(zv), NULL); } } else { jv_free(result); if (PHP_JQ_G(display_errors)) { PHP_JQ_ERR(E_WARNING, "filter parse error"); } ZVAL_BOOL(*return_value, 0); } }
void dump_operation(struct bytecode* bc, uint16_t* codeptr) { int pc = codeptr - bc->code; printf("%04d ", pc); const struct opcode_description* op = opcode_describe(bc->code[pc++]); printf("%s", op->name); if (op->length > 1) { uint16_t imm = bc->code[pc++]; if (op->op == CALL_JQ || op->op == TAIL_CALL_JQ) { for (int i=0; i<imm+1; i++) { uint16_t level = bc->code[pc++]; uint16_t idx = bc->code[pc++]; jv name; if (idx & ARG_NEWCLOSURE) { idx &= ~ARG_NEWCLOSURE; name = jv_object_get(jv_copy(getlevel(bc,level)->subfunctions[idx]->debuginfo), jv_string("name")); } else { name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,level)->debuginfo), jv_string("params")), idx); } printf(" %s:%d", jv_string_value(name), idx); jv_free(name); if (level) { printf("^%d", level); } } } else if (op->op == CALL_BUILTIN) { int func = bc->code[pc++]; jv name = jv_array_get(jv_copy(bc->globals->cfunc_names), func); printf(" %s", jv_string_value(name)); jv_free(name); } else if (op->flags & OP_HAS_BRANCH) { printf(" %04d", pc + imm); } else if (op->flags & OP_HAS_CONSTANT) { printf(" "); jv_dump(jv_array_get(jv_copy(bc->constants), imm), 0); } else if (op->flags & OP_HAS_VARIABLE) { uint16_t v = bc->code[pc++]; jv name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,imm)->debuginfo), jv_string("locals")), v); printf(" $%s:%d", jv_string_value(name), v); jv_free(name); if (imm) { printf("^%d", imm); } } else { printf(" %d", imm); } } }
static jv f_startswith(jv a, jv b) { int alen = jv_string_length_bytes(jv_copy(a)); int blen = jv_string_length_bytes(jv_copy(b)); jv ret; if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0) ret = jv_true(); else ret = jv_false(); jv_free(a); jv_free(b); return ret; }
static jv f_startswith(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) return jv_invalid_with_msg(jv_string("startswith() requires string inputs")); int alen = jv_string_length_bytes(jv_copy(a)); int blen = jv_string_length_bytes(jv_copy(b)); jv ret; if (blen <= alen && memcmp(jv_string_value(a), jv_string_value(b), blen) == 0) ret = jv_true(); else ret = jv_false(); jv_free(a); jv_free(b); return ret; }
static int process(jq_state *jq, jv value, int flags) { int ret = 14; // No valid results && -e -> exit(4) jq_start(jq, value, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); ret = 0; jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED; if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR; if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR; if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL) ret = 11; else ret = 0; jv_dump(result, dumpopts); } if (!(options & RAW_NO_LF)) printf("\n"); if (options & UNBUFFERED_OUTPUT) fflush(stdout); } if (jv_invalid_has_msg(jv_copy(result))) { // Uncaught jq exception jv msg = jv_invalid_get_msg(jv_copy(result)); if (jv_get_kind(msg) == JV_KIND_STRING) { fprintf(stderr, "jq: error: %s\n", jv_string_value(msg)); } else { msg = jv_dump_string(msg, 0); fprintf(stderr, "jq: error (not a string): %s\n", jv_string_value(msg)); } jv_free(msg); } jv_free(result); return ret; }
struct bytecode* jq_compile_args(const char* str, jv args) { assert(jv_get_kind(args) == JV_KIND_ARRAY); struct locfile locations; locfile_init(&locations, str, strlen(str)); block program; struct bytecode* bc = 0; int nerrors = jq_parse(&locations, &program); if (nerrors == 0) { for (int i=0; i<jv_array_length(jv_copy(args)); i++) { jv arg = jv_array_get(jv_copy(args), i); jv name = jv_object_get(jv_copy(arg), jv_string("name")); jv value = jv_object_get(arg, jv_string("value")); program = gen_var_binding(gen_const(value), jv_string_value(name), program); jv_free(name); } jv_free(args); 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; }
static void process(jv value, int flags) { jq_state *jq = NULL; jq_init(bc, value, &jq, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR; if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); jq_teardown(&jq); }
static jv f_endswith(jv a, jv b) { const char *astr = jv_string_value(a); const char *bstr = jv_string_value(b); size_t alen = jv_string_length_bytes(jv_copy(a)); size_t blen = jv_string_length_bytes(jv_copy(b)); jv ret;; if (alen < blen || memcmp(astr + (alen - blen), bstr, blen) != 0) ret = jv_false(); else ret = jv_true(); jv_free(a); jv_free(b); return ret; }
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); }
static jv f_endswith(jq_state *jq, jv a, jv b) { if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) return jv_invalid_with_msg(jv_string("endswith() requires string inputs")); const char *astr = jv_string_value(a); const char *bstr = jv_string_value(b); size_t alen = jv_string_length_bytes(jv_copy(a)); size_t blen = jv_string_length_bytes(jv_copy(b)); jv ret;; if (alen < blen || memcmp(astr + (alen - blen), bstr, blen) != 0) ret = jv_false(); else ret = jv_true(); jv_free(a); jv_free(b); return ret; }
static void print_error(jq_state *jq, jv value) { assert(!jv_is_valid(value)); jv msg = jv_invalid_get_msg(value); jv msg2; if (jv_get_kind(msg) == JV_KIND_STRING) msg2 = jv_string_fmt("jq: error: %s", jv_string_value(msg)); else msg2 = jv_string_fmt("jq: error: <unknown>"); jv_free(msg); if (jq->err_cb) jq->err_cb(jq->err_cb_data, msg2); else if (jv_get_kind(msg2) == JV_KIND_STRING) fprintf(stderr, "%s\n", jv_string_value(msg2)); else fprintf(stderr, "jq: error: %s\n", strerror(ENOMEM)); jv_free(msg2); }
static jv f_rtrimstr(jv input, jv right) { if (jv_get_kind(f_endswith(jv_copy(input), jv_copy(right))) == JV_KIND_TRUE) { jv res = jv_string_sized(jv_string_value(input), jv_string_length_bytes(jv_copy(input)) - jv_string_length_bytes(right)); jv_free(input); return res; } jv_free(right); return input; }
static int process_extra_opt(jq_state *jq, jv value, int flags, int dumpopts, jv_extra_opt *extra_opt) { int ret = 14; // No valid results && -e -> exit(4) jq_start(jq, value, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); ret = 0; jv_free(result); } else { if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL) ret = 11; else ret = 0; if (options & SEQ) priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY); jv_dump_extra_opt(result, dumpopts, extra_opt); } if (!(options & RAW_NO_LF)) priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY); if (options & UNBUFFERED_OUTPUT) fflush(stdout); } if (jv_invalid_has_msg(jv_copy(result))) { // Uncaught jq exception jv msg = jv_invalid_get_msg(jv_copy(result)); jv input_pos = jq_util_input_get_position(jq); if (jv_get_kind(msg) == JV_KIND_STRING) { fprintf(stderr, "jq: error (at %s): %s\n", jv_string_value(input_pos), jv_string_value(msg)); } else { msg = jv_dump_string(msg, 0); fprintf(stderr, "jq: error (at %s) (not a string): %s\n", jv_string_value(input_pos), jv_string_value(msg)); } ret = 5; jv_free(input_pos); jv_free(msg); } jv_free(result); return ret; }
int jq_compile_args(jq_state *jq, const char* str, jv args) { jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data); assert(jv_get_kind(args) == JV_KIND_ARRAY); struct locfile locations; locfile_init(&locations, jq, str, strlen(str)); block program; jq_reset(jq); if (jq->bc) { bytecode_free(jq->bc); jq->bc = 0; } int nerrors = jq_parse(&locations, &program); if (nerrors == 0) { int i; for (i=0; i<jv_array_length(jv_copy(args)); i++) { jv arg = jv_array_get(jv_copy(args), i); jv name = jv_object_get(jv_copy(arg), jv_string("name")); jv value = jv_object_get(arg, jv_string("value")); program = gen_var_binding(gen_const(value), jv_string_value(name), program); jv_free(name); } nerrors = builtins_bind(jq, &program); if (nerrors == 0) { nerrors = block_compile(program, &locations, &jq->bc); } } jv_free(args); if (nerrors) { jv s = jv_string_fmt("%d compile %s", nerrors, nerrors > 1 ? "errors" : "error"); if (jq->err_cb != NULL) jq->err_cb(jq->err_cb_data, s); else if (!jv_is_valid(s)) fprintf(stderr, "Error formatting jq compilation errors: %s\n", strerror(errno)); else fprintf(stderr, "%s\n", jv_string_value(s)); jv_free(s); } locfile_free(&locations); return jq->bc != NULL; }
static void php_jq_err_cb(void *data, jv err) { TSRMLS_FETCH(); if (jv_is_valid(err) && PHP_JQ_G(display_errors)) { jv dump = jv_dump_string(jv_copy(err), 0); if (jv_is_valid(dump)) { PHP_JQ_ERR(E_WARNING, jv_string_value(dump)); } jv_free(dump); } }
void dump_disassembly(int indent, struct bytecode* bc) { if (bc->nclosures > 0) { printf("%*s[params: ", indent, ""); jv params = jv_object_get(jv_copy(bc->debuginfo), jv_string("params")); for (int i=0; i<bc->nclosures; i++) { if (i) printf(", "); jv name = jv_array_get(jv_copy(params), i); printf("%s", jv_string_value(name)); jv_free(name); } jv_free(params); printf("]\n"); } dump_code(indent, bc); for (int i=0; i<bc->nsubfunctions; i++) { struct bytecode* subfn = bc->subfunctions[i]; jv name = jv_object_get(jv_copy(subfn->debuginfo), jv_string("name")); printf("%*s%s:%d:\n", indent, "", jv_string_value(name), i); jv_free(name); dump_disassembly(indent+2, subfn); } }
static void f_tonumber(jv input[], jv output[]) { if (jv_get_kind(input[0]) == JV_KIND_NUMBER) { output[0] = input[0]; } else if (jv_get_kind(input[0]) == JV_KIND_STRING) { jv parsed = jv_parse(jv_string_value(input[0])); if (!jv_is_valid(parsed)) { jv_free(input[0]); output[0] = parsed; } else if (jv_get_kind(parsed) != JV_KIND_NUMBER) { output[0] = jv_invalid_with_msg(jv_string_fmt("'%s' is not a number", jv_string_value(input[0]))); jv_free(input[0]); } else { jv_free(input[0]); output[0] = parsed; } } else { output[0] = jv_invalid_with_msg(jv_string_fmt("Cannot parse %s as a number", jv_kind_name(jv_get_kind(input[0])))); jv_free(input[0]); return; } }
static jv f_ltrimstr(jv input, jv left) { if (jv_get_kind(f_startswith(jv_copy(input), jv_copy(left))) != JV_KIND_TRUE) { jv_free(left); return input; } /* * FIXME It'd be better to share the suffix with the original input -- * that we could do, we just can't share prefixes. */ int prefixlen = jv_string_length_bytes(left); jv res = jv_string_sized(jv_string_value(input) + prefixlen, jv_string_length_bytes(jv_copy(input)) - prefixlen); jv_free(input); return res; }
static void process(jv value) { jq_init(bc, value); jv result; while (jv_is_valid(result = jq_next())) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length(jv_copy(result)), stdout); } else { int dumpopts = 0; if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); jq_teardown(); }
static int process(jq_state *jq, jv value, int flags) { int ret = 0; jq_start(jq, value, flags); jv result; while (jv_is_valid(result = jq_next(jq))) { if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) { fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout); if (jv_get_kind(result) == JV_KIND_FALSE) ret = 10; else if (jv_get_kind(result) == JV_KIND_NULL) ret = 11; else ret = 0; jv_free(result); } else { int dumpopts; /* Disable colour by default on Windows builds as Windows terminals tend not to display it correctly */ #ifdef WIN32 dumpopts = 0; #else dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0; #endif if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED; if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY; if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII; if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR; if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR; if (options & UNBUFFERED_OUTPUT) dumpopts |= JV_PRINT_UNBUFFERED; if (jv_get_kind(result) == JV_KIND_FALSE) ret = 10; else if (jv_get_kind(result) == JV_KIND_NULL) ret = 11; else ret = 0; jv_dump(result, dumpopts); } printf("\n"); } jv_free(result); return ret; }
// when is this called >1 time? std::vector<std::string> jqr_process(jq_state_ptr state, jv value) { std::vector<std::string> ret; jq_start(state.get(), value, 0); jv result = jq_next(state.get()); while (jv_is_valid(result)) { jv dumped = jv_dump_string(result, 0); // A lot of unnecessary copying here (char* to string to R - read // how one is *meant* to do this). const char *str = jv_string_value(dumped); std::string str_cpp(str); ret.push_back(str_cpp); result = jq_next(state.get()); } jv_free(result); return ret; }
std::vector<std::string> jqr_parse(jq_state_ptr state, jv_parser_ptr parser) { jv value = jv_parser_next(parser.get()); std::vector<std::string> ret; // This loop is not dealt with properly yet; will just take last. while (jv_is_valid(value)) { ret = jqr_process(state, value); value = jv_parser_next(parser.get()); } if (jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); const char * msg_str = jv_string_value(msg); jv_free(msg); jv_free(value); Rcpp::stop(msg_str); } else { jv_free(value); } return ret; }
jv jv_parse_sized(const char* string, int length) { struct jv_parser parser; parser_init(&parser); jv_parser_set_buf(&parser, string, length, 0); jv value = jv_parser_next(&parser); if (jv_is_valid(value)) { jv next = jv_parser_next(&parser); if (jv_is_valid(next)) { // multiple JSON values, we only wanted one jv_free(value); jv_free(next); value = jv_invalid_with_msg(jv_string("Unexpected extra JSON values")); } else if (jv_invalid_has_msg(jv_copy(next))) { // parser error after the first JSON value jv_free(value); value = next; } else { // a single valid JSON value jv_free(next); } } else if (jv_invalid_has_msg(jv_copy(value))) { // parse error, we'll return it } else { // no value at all jv_free(value); value = jv_invalid_with_msg(jv_string("Expected JSON value")); } parser_free(&parser); if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) { jv msg = jv_invalid_get_msg(value); value = jv_invalid_with_msg(jv_string_fmt("%s (while parsing '%s')", jv_string_value(msg), string)); jv_free(msg); } return value; }
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; }
static void run_jq_tests(jv lib_dirs, FILE *testdata) { char prog[4096]; char buf[4096]; struct err_data err_msg; int tests = 0, passed = 0, invalid = 0; unsigned int lineno = 0; int must_fail = 0; int check_msg = 0; jq_state *jq = NULL; jq = jq_init(); assert(jq); if (jv_get_kind(lib_dirs) == JV_KIND_NULL) lib_dirs = jv_array(); jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_dirs); while (1) { if (!fgets(prog, sizeof(prog), testdata)) break; lineno++; if (skipline(prog)) continue; if (checkfail(prog)) { must_fail = 1; check_msg = checkerrormsg(prog); jq_set_error_cb(jq, test_err_cb, &err_msg); continue; } if (prog[strlen(prog)-1] == '\n') prog[strlen(prog)-1] = 0; printf("Testing '%s' at line number %u\n", prog, lineno); int pass = 1; tests++; int compiled = jq_compile(jq, prog); if (must_fail) { jq_set_error_cb(jq, NULL, NULL); must_fail = 0; check_msg = 0; if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; } lineno++; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0; if (compiled) { printf("*** Test program compiled that should not have at line %u: %s\n", lineno, prog); invalid++; continue; } if (check_msg && strcmp(buf, err_msg.buf) != 0) { printf("*** Erroneous test program failed with wrong message (%s) at line %u: %s\n", err_msg.buf, lineno, prog); invalid++; } else { passed++; } continue; } if (!compiled) { printf("*** Test program failed to compile at line %u: %s\n", lineno, prog); invalid++; // skip past test data while (fgets(buf, sizeof(buf), testdata)) { lineno++; if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) break; } continue; } printf("Disassembly:\n"); jq_dump_disassembly(jq, 2); printf("\n"); if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; } lineno++; jv input = jv_parse(buf); if (!jv_is_valid(input)){ invalid++; continue; } jq_start(jq, input, JQ_DEBUG_TRACE); while (fgets(buf, sizeof(buf), testdata)) { lineno++; if (skipline(buf)) break; jv expected = jv_parse(buf); if (!jv_is_valid(expected)){ invalid++; continue; } jv actual = jq_next(jq); if (!jv_is_valid(actual)) { jv_free(actual); printf("*** Insufficient results for test at line number %u: %s\n", lineno, prog); pass = 0; break; } else if (!jv_equal(jv_copy(expected), jv_copy(actual))) { printf("*** Expected "); jv_dump(jv_copy(expected), 0); printf(", but got "); jv_dump(jv_copy(actual), 0); printf(" for test at line number %u: %s\n", lineno, prog); pass = 0; } jv as_string = jv_dump_string(jv_copy(expected), rand() & ~(JV_PRINT_COLOUR|JV_PRINT_REFCOUNT)); jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length_bytes(jv_copy(as_string))); assert(jv_equal(jv_copy(expected), jv_copy(reparsed))); jv_free(as_string); jv_free(reparsed); jv_free(expected); jv_free(actual); } if (pass) { jv extra = jq_next(jq); if (jv_is_valid(extra)) { printf("*** Superfluous result: "); jv_dump(extra, 0); printf(" for test at line number %u, %s\n", lineno, prog); pass = 0; } else { jv_free(extra); } } passed+=pass; } jq_teardown(&jq); printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid); if (passed != tests) exit(1); }
static void jv_test() { /// JSON parser regression tests { jv v = jv_parse("{\"a':\"12\"}"); assert(jv_get_kind(v) == JV_KIND_INVALID); v = jv_invalid_get_msg(v); assert(strcmp(jv_string_value(v), "Expected separator between values at line 1, column 9 (while parsing '{\"a':\"12\"}')") == 0); jv_free(v); } /// Arrays and numbers { jv a = jv_array(); assert(jv_get_kind(a) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(a)) == 0); assert(jv_array_length(jv_copy(a)) == 0); a = jv_array_append(a, jv_number(42)); assert(jv_array_length(jv_copy(a)) == 1); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); jv a2 = jv_array_append(jv_array(), jv_number(42)); assert(jv_equal(jv_copy(a), jv_copy(a))); assert(jv_equal(jv_copy(a2), jv_copy(a2))); assert(jv_equal(jv_copy(a), jv_copy(a2))); assert(jv_equal(jv_copy(a2), jv_copy(a))); jv_free(a2); a2 = jv_array_append(jv_array(), jv_number(19)); assert(!jv_equal(jv_copy(a), jv_copy(a2))); assert(!jv_equal(jv_copy(a2), jv_copy(a))); jv_free(a2); assert(jv_get_refcnt(a) == 1); a = jv_array_append(a, jv_copy(a)); assert(jv_get_refcnt(a) == 1); assert(jv_array_length(jv_copy(a)) == 2); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); for (int i=0; i<10; i++) { jv subarray = jv_array_get(jv_copy(a), 1); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv_free(subarray); } jv subarray = jv_array_get(jv_copy(a), 1); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv sub2 = jv_copy(subarray); sub2 = jv_array_append(sub2, jv_number(19)); assert(jv_get_kind(sub2) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(sub2)) == 2); assert(jv_number_value(jv_array_get(jv_copy(sub2), 0)) == 42); assert(jv_number_value(jv_array_get(jv_copy(sub2), 1)) == 19); assert(jv_get_kind(subarray) == JV_KIND_ARRAY); assert(jv_array_length(jv_copy(subarray)) == 1); assert(jv_number_value(jv_array_get(jv_copy(subarray), 0)) == 42); jv_free(subarray); void* before = sub2.u.ptr; sub2 = jv_array_append(sub2, jv_number(200)); void* after = sub2.u.ptr; assert(before == after); jv_free(sub2); jv a3 = jv_array_append(jv_copy(a), jv_number(19)); assert(jv_array_length(jv_copy(a3)) == 3); assert(jv_number_value(jv_array_get(jv_copy(a3), 0)) == 42); assert(jv_array_length(jv_array_get(jv_copy(a3), 1)) == 1); assert(jv_number_value(jv_array_get(jv_copy(a3), 2)) == 19); jv_free(a3); jv a4 = jv_array(); a4 = jv_array_append(a4, jv_number(1)); a4 = jv_array_append(a4, jv_number(2)); jv a5 = jv_copy(a4); a4 = jv_array_append(a4, jv_number(3)); a4 = jv_array_slice(a4, 0, 1); assert(jv_array_length(jv_copy(a4)) == 1); a4 = jv_array_append(a4, jv_number(4)); assert(jv_array_length(jv_copy(a4)) == 2); assert(jv_array_length(jv_copy(a5)) == 2); jv_free(a4); jv_free(a5); assert(jv_array_length(jv_copy(a)) == 2); assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42); assert(jv_array_length(jv_array_get(jv_copy(a), 1)) == 1); jv_dump(jv_copy(a), 0); printf("\n"); jv_free(a); } /// Strings { assert(jv_equal(jv_string("foo"), jv_string_sized("foo", 3))); char nasty[] = "foo\0"; jv shortstr = jv_string(nasty), longstr = jv_string_sized(nasty, sizeof(nasty)); assert(jv_string_length_bytes(jv_copy(shortstr)) == (int)strlen(nasty)); assert(jv_string_length_bytes(jv_copy(longstr)) == (int)sizeof(nasty)); jv_free(shortstr); jv_free(longstr); char a1s[] = "hello", a2s[] = "hello", bs[] = "goodbye"; jv a1 = jv_string(a1s), a2 = jv_string(a2s), b = jv_string(bs); assert(jv_equal(jv_copy(a1), jv_copy(a2))); assert(jv_equal(jv_copy(a2), jv_copy(a1))); assert(!jv_equal(jv_copy(a1), jv_copy(b))); assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a1))); assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a2))); assert(jv_string_hash(jv_copy(b)) != jv_string_hash(jv_copy(a1))); jv_free(a1); jv_free(a2); jv_free(b); assert(jv_equal(jv_string("hello42!"), jv_string_fmt("hello%d%s", 42, "!"))); char big[20000]; for (int i=0; i<(int)sizeof(big); i++) big[i] = 'a'; big[sizeof(big)-1] = 0; jv str = jv_string_fmt("%s", big); assert(jv_string_length_bytes(jv_copy(str)) == sizeof(big) - 1); assert(!strcmp(big, jv_string_value(str))); jv_free(str); } /// Objects { jv o1 = jv_object(); o1 = jv_object_set(o1, jv_string("foo"), jv_number(42)); o1 = jv_object_set(o1, jv_string("bar"), jv_number(24)); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24); jv o2 = jv_object_set(jv_copy(o1), jv_string("foo"), jv_number(420)); o2 = jv_object_set(o2, jv_string("bar"), jv_number(240)); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("foo"))) == 42); assert(jv_number_value(jv_object_get(jv_copy(o1), jv_string("bar"))) == 24); assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("foo"))) == 420); jv_free(o1); assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("bar"))) == 240); jv_dump(jv_copy(o2), 0); printf("\n"); jv_free(o2); } /// Compile errors { jq_state *jq = jq_init(); jq_compile_args(jq, "}", jv_array()); jq_teardown(&jq); } }