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* jvp_array_write(jv* a, int i) { assert(i >= 0); jvp_array* array = jvp_array_ptr(*a); int pos = i + jvp_array_offset(*a); if (pos < array->alloc_length && jvp_refcnt_unshared(a->u.ptr)) { // use existing array space for (int j = array->length; j <= pos; j++) { array->elements[j] = JV_NULL; } array->length = imax(pos + 1, array->length); a->size = imax(i + 1, a->size); return &array->elements[pos]; } else { // allocate a new array int new_length = imax(i + 1, jvp_array_length(*a)); jvp_array* new_array = jvp_array_alloc(ARRAY_SIZE_ROUND_UP(new_length)); int j; for (j = 0; j < jvp_array_length(*a); j++) { new_array->elements[j] = jv_copy(array->elements[j + jvp_array_offset(*a)]); } for (; j < new_length; j++) { new_array->elements[j] = JV_NULL; } new_array->length = new_length; jvp_array_free(*a); jv new_jv = {JV_KIND_ARRAY, 0, 0, new_length, {&new_array->refcnt}}; *a = new_jv; return &new_array->elements[i]; } }
jv jv_keys(jv x) { if (jv_get_kind(x) == JV_KIND_OBJECT) { int nkeys = jv_object_length(jv_copy(x)); jv* keys = malloc(sizeof(jv) * nkeys); int kidx = 0; jv_object_foreach(i, x) { keys[kidx++] = jv_object_iter_key(x, i); }
static void f_keys(jv input[], jv output[]) { if (jv_get_kind(input[0]) == JV_KIND_OBJECT) { int nkeys = jv_object_length(jv_copy(input[0])); jv* keys = malloc(sizeof(jv) * nkeys); int kidx = 0; jv_object_foreach(i, input[0]) { keys[kidx++] = jv_object_iter_key(input[0], i); }
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 jv f_minus(jv input, jv a, jv b) { jv_free(input); if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { return jv_number(jv_number_value(a) - jv_number_value(b)); } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { jv out = jv_array(); jv_array_foreach(a, i, x) { int include = 1; jv_array_foreach(b, j, y) { if (jv_equal(jv_copy(x), y)) { include = 0; break; } } if (include) out = jv_array_append(out, jv_copy(x)); jv_free(x); }
static jv type_error(jv bad, const char* msg) { char errbuf[15]; jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) %s", jv_kind_name(jv_get_kind(bad)), jv_dump_string_trunc(jv_copy(bad), errbuf, sizeof(errbuf)), msg)); jv_free(bad); return err; }
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); }
void stack_save(jq_state *jq, uint16_t* retaddr){ struct forkpoint* fork = forkable_stack_push(&jq->fork_stk, sizeof(struct forkpoint)); forkable_stack_save(&jq->data_stk, &fork->saved_data_stack); forkable_stack_save(&jq->frame_stk, &fork->saved_call_stack); fork->path_len = jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0; fork->subexp_nest = jq->subexp_nest; fork->return_address = retaddr; }
jv stack_pop(jq_state *jq) { jv* sval = stack_block(&jq->stk, jq->stk_top); jv val = *sval; if (!stack_pop_will_free(&jq->stk, jq->stk_top)) { val = jv_copy(val); } jq->stk_top = stack_pop_block(&jq->stk, jq->stk_top, sizeof(jv)); assert(jv_is_valid(val)); return val; }
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 int parse_slice(jv j, jv slice, int* pstart, int* pend) { // Array slices jv start_jv = jv_object_get(jv_copy(slice), jv_string("start")); jv end_jv = jv_object_get(slice, jv_string("end")); if (jv_get_kind(start_jv) == JV_KIND_NULL) { jv_free(start_jv); start_jv = jv_number(0); } int len; if (jv_get_kind(j) == JV_KIND_ARRAY) len = jv_array_length(jv_copy(j)); else len = jv_string_length_codepoints(jv_copy(j)); if (jv_get_kind(end_jv) == JV_KIND_NULL) { jv_free(end_jv); end_jv = jv_number(len); } if (jv_get_kind(start_jv) != JV_KIND_NUMBER || jv_get_kind(end_jv) != JV_KIND_NUMBER) { jv_free(start_jv); jv_free(end_jv); return 0; } else { double dstart = jv_number_value(start_jv); double dend = jv_number_value(end_jv); if (dstart < 0) dstart += len; if (dend < 0) dend += len; if (dstart < 0) dstart = 0; if (dstart > len) dstart = len; int start = (int)dstart; int end = (dend > len) ? len : (int)dend; // Ends are exclusive but e.g. 1 < 1.5 so :1.5 should be :2 not :1 if(end < dend) end += 1; if (end > len) end = len; if (end < start) end = start; assert(0 <= start && start <= end && end <= len); *pstart = start; *pend = end; return 1; } }
jv stack_pop(jq_state *jq) { data_stk_elem* s = forkable_stack_peek(&jq->data_stk); jv val = s->val; if (!forkable_stack_pop_will_free(&jq->data_stk)) { val = jv_copy(val); } forkable_stack_pop(&jq->data_stk); assert(jv_is_valid(val)); return val; }
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; }
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); } }
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; }
void stack_save(jq_state *jq, uint16_t* retaddr, struct stack_pos sp){ jq->fork_top = stack_push_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint)); struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top); fork->saved_data_stack = jq->stk_top; fork->saved_curr_frame = jq->curr_frame; fork->path_len = jv_get_kind(jq->path) == JV_KIND_ARRAY ? jv_array_length(jv_copy(jq->path)) : 0; fork->subexp_nest = jq->subexp_nest; fork->return_address = retaddr; jq->stk_top = sp.saved_data_stack; jq->curr_frame = sp.saved_curr_frame; }
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); } }
jv jv_array_get(jv j, int idx) { assert(jv_get_kind(j) == JV_KIND_ARRAY); jv* slot = jvp_array_read(j, idx); jv val; if (slot) { val = jv_copy(*slot); } else { val = jv_invalid(); } jv_free(j); return val; }
static void f_minus(jv input[], jv output[]) { jv_free(input[0]); jv a = input[2]; jv b = input[1]; if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { output[0] = jv_number(jv_number_value(a) - jv_number_value(b)); } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { jv out = jv_array(); for (int i=0; i<jv_array_length(jv_copy(a)); i++) { jv x = jv_array_get(jv_copy(a), i); int include = 1; for (int j=0; j<jv_array_length(jv_copy(b)); j++) { if (jv_equal(jv_copy(x), jv_array_get(jv_copy(b), j))) { include = 0; break; } } if (include) out = jv_array_append(out, jv_copy(x)); jv_free(x); } jv_free(a); jv_free(b); output[0] = out; } else { output[0] = jv_invalid_with_msg(jv_string_fmt("Attempted to subtract %s and %s", jv_kind_name(jv_get_kind(a)), jv_kind_name(jv_get_kind(b)))); jv_free(a); jv_free(b); } }
// assumes keys is a sorted array jv jv_dels(jv t, jv keys) { assert(jv_get_kind(keys) == JV_KIND_ARRAY); assert(jv_is_valid(t)); if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) { // no change } else if (jv_get_kind(t) == JV_KIND_ARRAY) { // extract slices, they must be handled differently jv orig_keys = keys; keys = jv_array(); jv new_array = jv_array(); jv starts = jv_array(), ends = jv_array(); jv_array_foreach(orig_keys, i, key) { if (jv_get_kind(key) == JV_KIND_NUMBER) { keys = jv_array_append(keys, key); } else if (jv_get_kind(key) == JV_KIND_OBJECT) { int start, end; if (parse_slice(t, key, &start, &end)) { starts = jv_array_append(starts, jv_number(start)); ends = jv_array_append(ends, jv_number(end)); } else { jv_free(new_array); jv_free(key); new_array = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); goto arr_out; } } else { jv_free(new_array); new_array = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", jv_kind_name(jv_get_kind(key)))); jv_free(key); goto arr_out; } } int kidx = 0; jv_array_foreach(t, i, elem) { int del = 0; while (kidx < jv_array_length(jv_copy(keys))) { int delidx = (int)jv_number_value(jv_array_get(jv_copy(keys), kidx)); if (i == delidx) { del = 1; } if (i < delidx) { break; } kidx++; } for (int sidx=0; !del && sidx<jv_array_length(jv_copy(starts)); sidx++) { if ((int)jv_number_value(jv_array_get(jv_copy(starts), sidx)) <= i && i < (int)jv_number_value(jv_array_get(jv_copy(ends), sidx))) { del = 1; } } if (!del) new_array = jv_array_append(new_array, elem); else jv_free(elem); }
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); } }
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; }
// assumes keys is a sorted array jv jv_dels(jv t, jv keys) { assert(jv_get_kind(keys) == JV_KIND_ARRAY); assert(jv_is_valid(t)); if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) { // no change } else if (jv_get_kind(t) == JV_KIND_ARRAY) { jv new_array = jv_array(); int kidx = 0; jv_array_foreach(t, i, elem) { int del = 0; while (kidx < jv_array_length(jv_copy(keys))) { jv nextdel = jv_array_get(jv_copy(keys), kidx); if (jv_get_kind(nextdel) != JV_KIND_NUMBER) { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s element of array", jv_kind_name(jv_get_kind(nextdel)))); jv_free(nextdel); jv_free(new_array); jv_free(elem); new_array = err; goto arr_out; // break twice } int delidx = (int)jv_number_value(nextdel); jv_free(nextdel); if (i == delidx) { del = 1; } if (i < delidx) { break; } kidx++; } if (!del) new_array = jv_array_append(new_array, elem); else jv_free(elem); }
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 jv* jvp_array_write(jv_nontrivial* a, int i) { assert(i >= 0); jvp_array* array = jvp_array_ptr(a); int pos = i + a->i[0]; if (pos < array->alloc_length) { // maybe we can update it in-place // FIXME: this "optimisation" can cause circular references #if 0 int can_write_past_end = array->length <= pos && /* the end of this array has never been used */ a->i[1] == array->length; /* the current slice sees the end of the array */ #endif int can_write_past_end = 0; if (can_write_past_end || jvp_refcnt_unshared(a)) { // extend the array for (int j = array->length; j <= pos; j++) { array->elements[j] = JV_NULL; } array->length = imax(pos + 1, array->length); a->i[1] = imax(pos + 1, a->i[1]); return &array->elements[pos]; } } int new_length = imax(i + 1, jvp_array_length(a)); jvp_array* new_array = jvp_array_alloc(ARRAY_SIZE_ROUND_UP(new_length)); int j; for (j = 0; j < jvp_array_length(a); j++) { new_array->elements[j] = jv_copy(array->elements[j + a->i[0]]); } for (; j < new_length; j++) { new_array->elements[j] = JV_NULL; } new_array->length = new_length; jvp_array_free(a); a->ptr = &new_array->refcnt; a->i[0] = 0; a->i[1] = new_length; return &new_array->elements[i]; }
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; }
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_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 jv_set(jv t, jv k, jv v) { if (!jv_is_valid(v)) { jv_free(t); jv_free(k); return v; } int isnull = jv_get_kind(t) == JV_KIND_NULL; if (jv_get_kind(k) == JV_KIND_STRING && (jv_get_kind(t) == JV_KIND_OBJECT || isnull)) { if (isnull) t = jv_object(); t = jv_object_set(t, k, v); } else if (jv_get_kind(k) == JV_KIND_NUMBER && (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); t = jv_array_set(t, (int)jv_number_value(k), v); } else if (jv_get_kind(k) == JV_KIND_OBJECT && (jv_get_kind(t) == JV_KIND_ARRAY || isnull)) { if (isnull) t = jv_array(); int start, end; if (parse_slice(t, k, &start, &end)) { if (jv_get_kind(v) == JV_KIND_ARRAY) { int array_len = jv_array_length(jv_copy(t)); assert(0 <= start && start <= end && end <= array_len); int slice_len = end - start; int insert_len = jv_array_length(jv_copy(v)); if (slice_len < insert_len) { // array is growing int shift = insert_len - slice_len; for (int i = array_len - 1; i >= end; i--) { t = jv_array_set(t, i + shift, jv_array_get(jv_copy(t), i)); } } else if (slice_len > insert_len) { // array is shrinking int shift = slice_len - insert_len; for (int i = end; i < array_len; i++) { t = jv_array_set(t, i - shift, jv_array_get(jv_copy(t), i)); } t = jv_array_slice(t, 0, array_len - shift); } for (int i=0; i < insert_len; i++) { t = jv_array_set(t, start + i, jv_array_get(jv_copy(v), i)); } jv_free(v); } else { jv_free(t); jv_free(v); t = jv_invalid_with_msg(jv_string_fmt("A slice of an array can only be assigned another array")); } } else { jv_free(t); jv_free(k); jv_free(v); t = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers")); } } else { jv err = jv_invalid_with_msg(jv_string_fmt("Cannot update field at %s index of %s", jv_kind_name(jv_get_kind(k)), jv_kind_name(jv_get_kind(t)))); jv_free(t); jv_free(k); jv_free(v); t = err; } return t; }