diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/builtin.c | 29 | ||||
-rw-r--r-- | src/execute.c | 15 | ||||
-rw-r--r-- | src/jq_test.c | 73 | ||||
-rw-r--r-- | src/jv.c | 651 | ||||
-rw-r--r-- | src/jv.h | 5 | ||||
-rw-r--r-- | src/jv_aux.c | 39 | ||||
-rw-r--r-- | src/jv_dtoa_tsd.c | 46 | ||||
-rw-r--r-- | src/jv_dtoa_tsd.h | 4 | ||||
-rw-r--r-- | src/jv_parse.c | 23 | ||||
-rw-r--r-- | src/jv_print.c | 37 | ||||
-rw-r--r-- | src/jv_type_private.h | 7 | ||||
-rw-r--r-- | src/parser.c | 1029 | ||||
-rw-r--r-- | src/parser.h | 16 | ||||
-rw-r--r-- | src/parser.y | 24 |
14 files changed, 1258 insertions, 740 deletions
diff --git a/src/builtin.c b/src/builtin.c index f52f56e2..b67f9c8d 100644 --- a/src/builtin.c +++ b/src/builtin.c @@ -90,8 +90,11 @@ static jv f_plus(jq_state *jq, jv input, jv a, jv b) { jv_free(b); return a; } else if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { - return jv_number(jv_number_value(a) + + jv r = jv_number(jv_number_value(a) + jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) { return jv_string_concat(a, b); } else if (jv_get_kind(a) == JV_KIND_ARRAY && jv_get_kind(b) == JV_KIND_ARRAY) { @@ -274,7 +277,10 @@ static jv f_rtrimstr(jq_state *jq, jv input, jv right) { static jv f_minus(jq_state *jq, 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)); + jv r = jv_number(jv_number_value(a) - jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; } 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) { @@ -302,7 +308,10 @@ static jv f_multiply(jq_state *jq, jv input, jv a, jv b) { jv_kind bk = jv_get_kind(b); jv_free(input); if (ak == JV_KIND_NUMBER && bk == JV_KIND_NUMBER) { - return jv_number(jv_number_value(a) * jv_number_value(b)); + jv r = jv_number(jv_number_value(a) * jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; } else if ((ak == JV_KIND_STRING && bk == JV_KIND_NUMBER) || (ak == JV_KIND_NUMBER && bk == JV_KIND_STRING)) { jv str = a; @@ -336,7 +345,10 @@ static jv f_divide(jq_state *jq, jv input, jv a, jv b) { if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { if (jv_number_value(b) == 0.0) return type_error2(a, b, "cannot be divided because the divisor is zero"); - return jv_number(jv_number_value(a) / jv_number_value(b)); + jv r = jv_number(jv_number_value(a) / jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) { return jv_string_split(a, b); } else { @@ -349,7 +361,10 @@ static jv f_mod(jq_state *jq, jv input, jv a, jv b) { if (jv_get_kind(a) == JV_KIND_NUMBER && jv_get_kind(b) == JV_KIND_NUMBER) { if ((intmax_t)jv_number_value(b) == 0) return type_error2(a, b, "cannot be divided (remainder) because the divisor is zero"); - return jv_number((intmax_t)jv_number_value(a) % (intmax_t)jv_number_value(b)); + jv r = jv_number((intmax_t)jv_number_value(a) % (intmax_t)jv_number_value(b)); + jv_free(a); + jv_free(b); + return r; } else { return type_error2(a, b, "cannot be divided (remainder)"); } @@ -440,7 +455,9 @@ static jv f_length(jq_state *jq, jv input) { } else if (jv_get_kind(input) == JV_KIND_STRING) { return jv_number(jv_string_length_codepoints(input)); } else if (jv_get_kind(input) == JV_KIND_NUMBER) { - return jv_number(fabs(jv_number_value(input))); + jv r = jv_number(fabs(jv_number_value(input))); + jv_free(input); + return r; } else if (jv_get_kind(input) == JV_KIND_NULL) { jv_free(input); return jv_number(0); diff --git a/src/execute.c b/src/execute.c index 65c6bc77..fd2ab2c7 100644 --- a/src/execute.c +++ b/src/execute.c @@ -509,21 +509,25 @@ jv jq_next(jq_state *jq) { uint16_t v = *pc++; jv* var = frame_local_var(jq, v, level); jv max = stack_pop(jq); - if (raising) goto do_backtrack; + if (raising) { + jv_free(max); + goto do_backtrack; + } if (jv_get_kind(*var) != JV_KIND_NUMBER || jv_get_kind(max) != JV_KIND_NUMBER) { set_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))) { + } else if (jv_number_value(*var) >= jv_number_value(max)) { /* finished iterating */ + jv_free(max); goto do_backtrack; } else { - jv curr = jv_copy(*var); + jv curr = *var; *var = jv_number(jv_number_value(*var) + 1); struct stack_pos spos = stack_get_pos(jq); - stack_push(jq, jv_copy(max)); + stack_push(jq, max); stack_save(jq, pc - 3, spos); stack_push(jq, curr); @@ -1010,6 +1014,9 @@ jq_state *jq_init(void) { jq->attrs = jv_object(); jq->path = jv_null(); jq->value_at_path = jv_null(); + + jq->nomem_handler = NULL; + jq->nomem_handler_data = NULL; return jq; } diff --git a/src/jq_test.c b/src/jq_test.c index 7a396b94..2b40d4d6 100644 --- a/src/jq_test.c +++ b/src/jq_test.c @@ -6,20 +6,32 @@ #include "jq.h" static void jv_test(); -static void run_jq_tests(jv, int, FILE *); +static void run_jq_tests(jv, int, FILE *, int, int); int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) { FILE *testdata = stdin; + int skip = -1; + int take = -1; jv_test(); if (argc > 0) { - testdata = fopen(argv[0], "r"); - if (!testdata) { - perror("fopen"); - exit(1); + for(int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--skip")) { + skip = atoi(argv[i+1]); + i++; + } else if (!strcmp(argv[i], "--take")) { + take = atoi(argv[i+1]); + i++; + } else { + testdata = fopen(argv[i], "r"); + if (!testdata) { + perror("fopen"); + exit(1); + } + } } } - run_jq_tests(libdirs, verbose, testdata); + run_jq_tests(libdirs, verbose, testdata, skip, take); return 0; } @@ -53,7 +65,7 @@ static void test_err_cb(void *data, jv e) { jv_free(e); } -static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata) { +static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata, int skip, int take) { char prog[4096]; char buf[4096]; struct err_data err_msg; @@ -63,6 +75,9 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata) { int check_msg = 0; jq_state *jq = NULL; + int tests_to_skip = skip; + int tests_to_take = take; + jq = jq_init(); assert(jq); if (jv_get_kind(lib_dirs) == JV_KIND_NULL) @@ -80,6 +95,34 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata) { continue; } if (prog[strlen(prog)-1] == '\n') prog[strlen(prog)-1] = 0; + + if (skip > 0) { + skip--; + + // skip past test data + while (fgets(buf, sizeof(buf), testdata)) { + lineno++; + if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) + break; + } + + must_fail = 0; + check_msg = 0; + + continue; + } else if (skip == 0) { + printf("Skipped %d tests\n", tests_to_skip); + skip = -1; + } + + if (take > 0) { + take--; + } else if (take == 0) { + printf("Hit the number of tests limit (%d), breaking\n", tests_to_take); + take = -1; + break; + } + printf("Testing '%s' at line number %u\n", prog, lineno); int pass = 1; tests++; @@ -179,7 +222,21 @@ static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata) { passed+=pass; } jq_teardown(&jq); - printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid); + + int total_skipped = tests_to_skip > 0 ? tests_to_skip : 0; + + if (skip > 0) { + total_skipped = tests_to_skip - skip; + } + + printf("%d of %d tests passed (%d malformed, %d skipped)\n", + passed, tests, invalid, total_skipped); + + if (skip > 0) { + printf("WARN: skipped past the end of file, exiting with status 2\n"); + exit(2); + } + if (passed != tests) exit(1); } @@ -13,6 +13,15 @@ #include "jv_unicode.h" #include "util.h" +#include "jv_dtoa.h" +#include "jv_dtoa_tsd.h" + +// this means that we will manage the space for the struct +#define DECNUMDIGITS 1 +#include "decNumber/decNumber.h" + +#include "jv_type_private.h" + /* * Internal refcounting helpers */ @@ -37,14 +46,33 @@ static int jvp_refcnt_unshared(jv_refcnt* c) { return c->count == 1; } -/* - * Simple values (true, false, null) - */ +#define KIND_MASK 0xF +#define PFLAGS_MASK 0xF0 +#define PTYPE_MASK 0x70 + +typedef enum { + JVP_PAYLOAD_NONE = 0, + JVP_PAYLOAD_ALLOCATED = 0x80, +} payload_flags; + +#define JVP_MAKE_PFLAGS(ptype, allocated) ((((ptype) << 4) & PTYPE_MASK) | ((allocated) ? JVP_PAYLOAD_ALLOCATED : 0)) +#define JVP_MAKE_FLAGS(kind, pflags) ((kind & KIND_MASK) | (pflags & PFLAGS_MASK)) + +#define JVP_FLAGS(j) ((j).kind_flags) +#define JVP_KIND(j) (JVP_FLAGS(j) & KIND_MASK) -#define KIND_MASK 0xf +#define JVP_HAS_FLAGS(j, flags) (JVP_FLAGS(j) == flags) +#define JVP_HAS_KIND(j, kind) (JVP_KIND(j) == kind) + +#define JVP_IS_ALLOCATED(j) (j.kind_flags & JVP_PAYLOAD_ALLOCATED) + +#define JVP_FLAGS_NULL JVP_MAKE_FLAGS(JV_KIND_NULL, JVP_PAYLOAD_NONE) +#define JVP_FLAGS_INVALID JVP_MAKE_FLAGS(JV_KIND_INVALID, JVP_PAYLOAD_NONE) +#define JVP_FLAGS_FALSE JVP_MAKE_FLAGS(JV_KIND_FALSE, JVP_PAYLOAD_NONE) +#define JVP_FLAGS_TRUE JVP_MAKE_FLAGS(JV_KIND_TRUE, JVP_PAYLOAD_NONE) jv_kind jv_get_kind(jv x) { - return x.kind_flags & KIND_MASK; + return JVP_KIND(x); } const char* jv_kind_name(jv_kind k) { @@ -62,10 +90,10 @@ const char* jv_kind_name(jv_kind k) { return "<unknown>"; } -static const jv JV_NULL = {JV_KIND_NULL, 0, 0, 0, {0}}; -static const jv JV_INVALID = {JV_KIND_INVALID, 0, 0, 0, {0}}; -static const jv JV_FALSE = {JV_KIND_FALSE, 0, 0, 0, {0}}; -static const jv JV_TRUE = {JV_KIND_TRUE, 0, 0, 0, {0}}; +const jv JV_NULL = {JVP_FLAGS_NULL, 0, 0, 0, {0}}; +const jv JV_INVALID = {JVP_FLAGS_INVALID, 0, 0, 0, {0}}; +const jv JV_FALSE = {JVP_FLAGS_FALSE, 0, 0, 0, {0}}; +const jv JV_TRUE = {JVP_FLAGS_TRUE, 0, 0, 0, {0}}; jv jv_true() { return JV_TRUE; @@ -87,19 +115,21 @@ jv jv_bool(int x) { * Invalid objects, with optional error messages */ +#define JVP_FLAGS_INVALID_MSG JVP_MAKE_FLAGS(JV_KIND_INVALID, JVP_PAYLOAD_ALLOCATED) + typedef struct { jv_refcnt refcnt; jv errmsg; } jvp_invalid; jv jv_invalid_with_msg(jv err) { - if (jv_get_kind(err) == JV_KIND_NULL) + if (JVP_HAS_KIND(err, JV_KIND_NULL)) return JV_INVALID; jvp_invalid* i = jv_mem_alloc(sizeof(jvp_invalid)); i->refcnt = JV_REFCNT_INIT; i->errmsg = err; - jv x = {JV_KIND_INVALID, 0, 0, 0, {&i->refcnt}}; + jv x = {JVP_FLAGS_INVALID_MSG, 0, 0, 0, {&i->refcnt}}; return x; } @@ -108,26 +138,30 @@ jv jv_invalid() { } jv jv_invalid_get_msg(jv inv) { - assert(jv_get_kind(inv) == JV_KIND_INVALID); + assert(JVP_HAS_KIND(inv, JV_KIND_INVALID)); + jv x; - if (inv.u.ptr == 0) - x = jv_null(); - else + if (JVP_HAS_FLAGS(inv, JVP_FLAGS_INVALID_MSG)) { x = jv_copy(((jvp_invalid*)inv.u.ptr)->errmsg); + } + else { + x = jv_null(); + } + jv_free(inv); return x; } int jv_invalid_has_msg(jv inv) { - jv msg = jv_invalid_get_msg(inv); - int r = jv_get_kind(msg) != JV_KIND_NULL; - jv_free(msg); + assert(JVP_HAS_KIND(inv, JV_KIND_INVALID)); + int r = JVP_HAS_FLAGS(inv, JVP_FLAGS_INVALID_MSG); + jv_free(inv); return r; } static void jvp_invalid_free(jv x) { - assert(jv_get_kind(x) == JV_KIND_INVALID); - if (x.u.ptr != 0 && jvp_refcnt_dec(x.u.ptr)) { + assert(JVP_HAS_KIND(x, JV_KIND_INVALID)); + if (JVP_HAS_FLAGS(x, JVP_FLAGS_INVALID_MSG) && jvp_refcnt_dec(x.u.ptr)) { jv_free(((jvp_invalid*)x.u.ptr)->errmsg); jv_mem_free(x.u.ptr); } @@ -137,20 +171,265 @@ static void jvp_invalid_free(jv x) { * Numbers */ +enum { + JVP_NUMBER_NATIVE = 0, + JVP_NUMBER_DECIMAL = 1 +}; + +#define JV_NUMBER_SIZE_INIT (0) +#define JV_NUMBER_SIZE_CONVERTED (1) + +#define JVP_FLAGS_NUMBER_NATIVE JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_NATIVE, 0)) +#define JVP_FLAGS_NUMBER_NATIVE_STR JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_NATIVE, 1)) +#define JVP_FLAGS_NUMBER_LITERAL JVP_MAKE_FLAGS(JV_KIND_NUMBER, JVP_MAKE_PFLAGS(JVP_NUMBER_DECIMAL, 1)) + +#define STR(x) #x +#define XSTR(x) STR(x) +#define DBL_MAX_STR XSTR(DBL_MAX) +#define DBL_MIN_STR "-" XSTR(DBL_MAX) + +// the decimal precision of binary double +#define BIN64_DEC_PRECISION (17) +#define DEC_NUMBER_STRING_GUARD (14) + +#include <pthread.h> + +static pthread_key_t dec_ctx_key; +static pthread_key_t dec_ctx_dbl_key; +static pthread_once_t dec_ctx_once = PTHREAD_ONCE_INIT; + +#define DEC_CONTEXT() tsd_dec_ctx_get(&dec_ctx_key) +#define DEC_CONTEXT_TO_DOUBLE() tsd_dec_ctx_get(&dec_ctx_dbl_key) + +// atexit finalizer to clean up the tsd dec contexts if main() exits +// without having called pthread_exit() +static void tsd_dec_ctx_fini() { + jv_mem_free(pthread_getspecific(dec_ctx_key)); + jv_mem_free(pthread_getspecific(dec_ctx_dbl_key)); + pthread_setspecific(dec_ctx_key, NULL); + pthread_setspecific(dec_ctx_dbl_key, NULL); +} + +static void tsd_dec_ctx_init() { + if (pthread_key_create(&dec_ctx_key, jv_mem_free) != 0) { + fprintf(stderr, "error: cannot create thread specific key"); + abort(); + } + if (pthread_key_create(&dec_ctx_dbl_key, jv_mem_free) != 0) { + fprintf(stderr, "error: cannot create thread specific key"); + abort(); + } + atexit(tsd_dec_ctx_fini); +} + +static decContext* tsd_dec_ctx_get(pthread_key_t *key) { + pthread_once(&dec_ctx_once, tsd_dec_ctx_init); // cannot fail + decContext *ctx = (decContext*)pthread_getspecific(*key); + if (ctx) { + return ctx; + } + + decContext _ctx = { + 0, + DEC_MAX_EMAX, + DEC_MIN_EMAX, + DEC_ROUND_HALF_UP, + 0, /*no errors*/ + 0, /*status*/ + 0, /*no clamping*/ + }; + if (key == &dec_ctx_key) { + _ctx.digits = DEC_MAX_DIGITS; + } else if (key == &dec_ctx_dbl_key) { + _ctx.digits = BIN64_DEC_PRECISION; + } + + ctx = malloc(sizeof(decContext)); + if (ctx) { + *ctx = _ctx; + if (pthread_setspecific(*key, ctx) != 0) { + fprintf(stderr, "error: cannot store thread specific data"); + abort(); + } + } + return ctx; +} + +typedef struct { + jv_refcnt refcnt; + double num_double; + char * literal_data; + decNumber num_decimal; // must be the last field in the structure for memory management +} jvp_literal_number; + +typedef struct { + decNumber number; + decNumberUnit units[1]; +} decNumberSingle; + +typedef struct { + decNumber number; + decNumberUnit units[BIN64_DEC_PRECISION]; +} decNumberDoublePrecision; + + +static inline int jvp_number_is_literal(jv n) { + assert(JVP_HAS_KIND(n, JV_KIND_NUMBER)); + return JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL); +} + +static jvp_literal_number* jvp_literal_number_ptr(jv j) { + assert(JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL)); + return (jvp_literal_number*)j.u.ptr; +} + +static decNumber* jvp_dec_number_ptr(jv j) { + assert(JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL)); + return &(((jvp_literal_number*)j.u.ptr)->num_decimal); +} + +static jvp_literal_number* jvp_literal_number_alloc(unsigned literal_length) { + + /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */ + int units = ((literal_length+DECDPUN-1)/DECDPUN); + + jvp_literal_number* n = jv_mem_alloc( + sizeof(jvp_literal_number) + + sizeof(decNumberUnit) * units + ); + + return n; +} + +static jv jvp_literal_number_new(const char * literal) { + + jvp_literal_number * n = jvp_literal_number_alloc(strlen(literal)); + + n->refcnt = JV_REFCNT_INIT; + n->literal_data = NULL; + decContext *ctx = DEC_CONTEXT(); + decNumberFromString(&n->num_decimal, literal, ctx); + n->num_double = NAN; + + if (ctx->status & DEC_Conversion_syntax) { + jv_mem_free(n); + return JV_INVALID; + } + + jv r = {JVP_FLAGS_NUMBER_LITERAL, 0, 0, JV_NUMBER_SIZE_INIT, {&n->refcnt}}; + return r; +} + +static double jvp_literal_number_to_double(jv j) { + assert(JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL)); + + decNumber *p_dec_number = jvp_dec_number_ptr(j); + decNumberDoublePrecision dec_double; + char literal[BIN64_DEC_PRECISION + DEC_NUMBER_STRING_GUARD + 1]; + + // reduce the number to the shortest possible form + // while also making sure than no more than BIN64_DEC_PRECISION + // digits are used (dec_context_to_double) + decNumberReduce(&dec_double.number, p_dec_number, DEC_CONTEXT_TO_DOUBLE()); + + decNumberToString(&dec_double.number, literal); + + char *end; + return jvp_strtod(tsd_dtoa_context_get(), literal, &end); +} + + +static int jvp_number_equal(jv a, jv b) { + return jvp_number_cmp(a, b) == 0; +} + +static const char* jvp_literal_number_literal(jv n) { + assert(JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL)); + decNumber *pdec = jvp_dec_number_ptr(n); + jvp_literal_number* plit = jvp_literal_number_ptr(n); + + if (decNumberIsNaN(pdec)) { + return "null"; + } + + if (decNumberIsInfinite(pdec)) { + // For backward compatibiltiy. + if (decNumberIsNegative(pdec)) { + return DBL_MIN_STR; + } else { + return DBL_MAX_STR; + } + } + + if (plit->literal_data == NULL) { + int len = jvp_dec_number_ptr(n)->digits + 14; + plit->literal_data = jv_mem_alloc(len); + + // Preserve the actual precision as we have parsed it + // don't do decNumberTrim(pdec); + + decNumberToString(pdec, plit->literal_data); + } + + return plit->literal_data; +} + +int jv_number_has_literal(jv n) { + assert(JVP_HAS_KIND(n, JV_KIND_NUMBER)); + return JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL); +} + +const char* jv_number_get_literal(jv n) { + assert(JVP_HAS_KIND(n, JV_KIND_NUMBER)); + + if (JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL)) { + return jvp_literal_number_literal(n); + } else { + return NULL; + } +} + +static void jvp_number_free(jv j) { + assert(JVP_HAS_KIND(j, JV_KIND_NUMBER)); + if (JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL) && jvp_refcnt_dec(j.u.ptr)) { + jvp_literal_number* n = jvp_literal_number_ptr(j); + if (n->literal_data) { + jv_mem_free(n->literal_data); + } + jv_mem_free(n); + } +} + +jv jv_number_with_literal(const char * literal) { + return jvp_literal_number_new(literal); +} + jv jv_number(double x) { - jv j = {JV_KIND_NUMBER, 0, 0, 0, {.number = x}}; + jv j = {JVP_FLAGS_NUMBER_NATIVE, 0, 0, 0, {.number = x}}; return j; } double jv_number_value(jv j) { - assert(jv_get_kind(j) == JV_KIND_NUMBER); - return j.u.number; + assert(JVP_HAS_KIND(j, JV_KIND_NUMBER)); + if (JVP_HAS_FLAGS(j, JVP_FLAGS_NUMBER_LITERAL)) { + jvp_literal_number* n = jvp_literal_number_ptr(j); + + if (j.size != JV_NUMBER_SIZE_CONVERTED) { + n->num_double = jvp_literal_number_to_double(j); + j.size = JV_NUMBER_SIZE_CONVERTED; + } + + return n->num_double; + } else { + return j.u.number; + } } int jv_is_integer(jv j){ - if(jv_get_kind(j) != JV_KIND_NUMBER){ + if(!JVP_HAS_KIND(j, JV_KIND_NUMBER)){ return 0; } + double x = jv_number_value(j); if(x != x || x > INT_MAX || x < INT_MIN){ return 0; @@ -159,11 +438,53 @@ int jv_is_integer(jv j){ return x == (int)x; } +int jvp_number_is_nan(jv n) { + assert(JVP_HAS_KIND(n, JV_KIND_NUMBER)); + + if (JVP_HAS_FLAGS(n, JVP_FLAGS_NUMBER_LITERAL)) { + decNumber *pdec = jvp_dec_number_ptr(n); + return decNumberIsNaN(pdec); + } else { + return n.u.number != n.u.number; + } +} + +int jvp_number_cmp(jv a, jv b) { + assert(JVP_HAS_KIND(a, JV_KIND_NUMBER)); + assert(JVP_HAS_KIND(b, JV_KIND_NUMBER)); + + if(JVP_HAS_FLAGS(a, JVP_FLAGS_NUMBER_LITERAL) && JVP_HAS_FLAGS(b, JVP_FLAGS_NUMBER_LITERAL)) { + decNumberSingle res; + decNumberCompare(&res.number, + jvp_dec_number_ptr(a), + jvp_dec_number_ptr(b), + DEC_CONTEXT() + ); + if (decNumberIsZero(&res.number)) { + return 0; + } else if (decNumberIsNegative(&res.number)) { + return -1; + } else { + return 1; + } + } else { + double da = jv_number_value(a), db = jv_number_value(b); + if (da < db) { + return -1; + } else if (da == db) { + return 0; + } else { + return 1; + } + } +} + /* * Arrays (internal helpers) */ #define ARRAY_SIZE_ROUND_UP(n) (((n)*3)/2) +#define JVP_FLAGS_ARRAY JVP_MAKE_FLAGS(JV_KIND_ARRAY, JVP_PAYLOAD_ALLOCATED) static int imax(int a, int b) { if (a>b) return a; @@ -178,7 +499,7 @@ typedef struct { } jvp_array; static jvp_array* jvp_array_ptr(jv a) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); return (jvp_array*)a.u.ptr; } @@ -191,12 +512,12 @@ static jvp_array* jvp_array_alloc(unsigned size) { } static jv jvp_array_new(unsigned size) { - jv r = {JV_KIND_ARRAY, 0, 0, 0, {&jvp_array_alloc(size)->refcnt}}; + jv r = {JVP_FLAGS_ARRAY, 0, 0, 0, {&jvp_array_alloc(size)->refcnt}}; return r; } static void jvp_array_free(jv a) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); if (jvp_refcnt_dec(a.u.ptr)) { jvp_array* array = jvp_array_ptr(a); for (int i=0; i<array->length; i++) { @@ -207,17 +528,17 @@ static void jvp_array_free(jv a) { } static int jvp_array_length(jv a) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); return a.size; } static int jvp_array_offset(jv a) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); return a.offset; } static jv* jvp_array_read(jv a, int i) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); if (i >= 0 && i < jvp_array_length(a)) { jvp_array* array = jvp_array_ptr(a); assert(i + jvp_array_offset(a) < array->length); @@ -254,7 +575,7 @@ static jv* jvp_array_write(jv* a, int i) { } new_array->length = new_length; jvp_array_free(*a); - jv new_jv = {JV_KIND_ARRAY, 0, 0, new_length, {&new_array->refcnt}}; + jv new_jv = {JVP_FLAGS_ARRAY, 0, 0, new_length, {&new_array->refcnt}}; *a = new_jv; return &new_array->elements[i]; } @@ -285,8 +606,33 @@ static void jvp_clamp_slice_params(int len, int *pstart, int *pend) if (*pend < *pstart) *pend = *pstart; } + +static int jvp_array_contains(jv a, jv b) { + int r = 1; + jv_array_foreach(b, bi, belem) { + int ri = 0; + jv_array_foreach(a, ai, aelem) { + if (jv_contains(aelem, jv_copy(belem))) { + ri = 1; + break; + } + } + jv_free(belem); + if (!ri) { + r = 0; + break; + } + } + return r; +} + + +/* + * Public + */ + static jv jvp_array_slice(jv a, int start, int end) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); int len = jvp_array_length(a); jvp_clamp_slice_params(len, &start, &end); assert(0 <= start && start <= end && end <= len); @@ -323,14 +669,14 @@ jv jv_array() { } int jv_array_length(jv j) { - assert(jv_get_kind(j) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(j, JV_KIND_ARRAY)); int len = jvp_array_length(j); jv_free(j); return len; } jv jv_array_get(jv j, int idx) { - assert(jv_get_kind(j) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(j, JV_KIND_ARRAY)); jv* slot = jvp_array_read(j, idx); jv val; if (slot) { @@ -343,7 +689,7 @@ jv jv_array_get(jv j, int idx) { } jv jv_array_set(jv j, int idx, jv val) { - assert(jv_get_kind(j) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(j, JV_KIND_ARRAY)); if (idx < 0) idx = jvp_array_length(j) + idx; @@ -365,8 +711,8 @@ jv jv_array_append(jv j, jv val) { } jv jv_array_concat(jv a, jv b) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); - assert(jv_get_kind(b) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); + assert(JVP_HAS_KIND(b, JV_KIND_ARRAY)); // FIXME: could be faster jv_array_foreach(b, i, elem) { @@ -377,44 +723,22 @@ jv jv_array_concat(jv a, jv b) { } jv jv_array_slice(jv a, int start, int end) { - assert(jv_get_kind(a) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(a, JV_KIND_ARRAY)); // copy/free of a coalesced return jvp_array_slice(a, start, end); } -int jv_array_contains(jv a, jv b) { - int r = 1; - jv_array_foreach(b, bi, belem) { - int ri = 0; - jv_array_foreach(a, ai, aelem) { - if (jv_contains(aelem, jv_copy(belem))) { - ri = 1; - break; - } - } - jv_free(belem); - if (!ri) { - r = 0; - break; - } - } - jv_free(a); - jv_free(b); - return r; -} - jv jv_array_indexes(jv a, jv b) { jv res = jv_array(); int idx = -1; jv_array_foreach(a, ai, aelem) { + jv_free(aelem); jv_array_foreach(b, bi, belem) { - // quieten compiler warnings about aelem not being used... by - // using it - if ((bi == 0 && !jv_equal(jv_copy(aelem), jv_copy(belem))) || - (bi > 0 && !jv_equal(jv_array_get(jv_copy(a), ai + bi), jv_copy(belem)))) + if (!jv_equal(jv_array_get(jv_copy(a), ai + bi), jv_copy(belem))) idx = -1; else if (bi == 0 && idx == -1) idx = ai; + jv_free(belem); } if (idx > -1) res = jv_array_append(res, jv_number(idx)); @@ -425,11 +749,12 @@ jv jv_array_indexes(jv a, jv b) { return res; } - /* * Strings (internal helpers) */ +#define JVP_FLAGS_STRING JVP_MAKE_FLAGS(JV_KIND_STRING, JVP_PAYLOAD_ALLOCATED) + typedef struct { jv_refcnt refcnt; uint32_t hash; @@ -441,7 +766,7 @@ typedef struct { } jvp_string; static jvp_string* jvp_string_ptr(jv a) { - assert(jv_get_kind(a) == JV_KIND_STRING); + assert(JVP_HAS_KIND(a, JV_KIND_STRING)); return (jvp_string*)a.u.ptr; } @@ -473,7 +798,7 @@ static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) { length = out - s->data; s->data[length] = 0; s->length_hashed = length << 1; - jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; return r; } @@ -484,7 +809,7 @@ static jv jvp_string_new(const char* data, uint32_t length) { if (data != NULL) memcpy(s->data, data, length); s->data[length] = 0; - jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; return r; } @@ -492,7 +817,7 @@ static jv jvp_string_empty_new(uint32_t length) { jvp_string* s = jvp_string_alloc(length); s->length_hashed = 0; memset(s->data, 0, length); - jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&s->refcnt}}; return r; } @@ -535,7 +860,7 @@ static jv jvp_string_append(jv string, const char* data, uint32_t len) { memcpy(news->data + currlen, data, len); news->data[currlen + len] = 0; jvp_string_free(string); - jv r = {JV_KIND_STRING, 0, 0, 0, {&news->refcnt}}; + jv r = {JVP_FLAGS_STRING, 0, 0, 0, {&news->refcnt}}; return r; } } @@ -602,9 +927,10 @@ static uint32_t jvp_string_hash(jv jstr) { return h1; } + static int jvp_string_equal(jv a, jv b) { - assert(jv_get_kind(a) == JV_KIND_STRING); - assert(jv_get_kind(b) == JV_KIND_STRING); + assert(JVP_HAS_KIND(a, JV_KIND_STRING)); + assert(JVP_HAS_KIND(b, JV_KIND_STRING)); jvp_string* stra = jvp_string_ptr(a); jvp_string* strb = jvp_string_ptr(b); if (jvp_string_length(stra) != jvp_string_length(strb)) return 0; @@ -631,14 +957,14 @@ jv jv_string(const char* str) { } int jv_string_length_bytes(jv j) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); int r = jvp_string_length(jvp_string_ptr(j)); jv_free(j); return r; } int jv_string_length_codepoints(jv j) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); const char* i = jv_string_value(j); const char* end = i + jv_string_length_bytes(jv_copy(j)); int c = 0, len = 0; @@ -649,8 +975,8 @@ int jv_string_length_codepoints(jv j) { jv jv_string_indexes(jv j, jv k) { - assert(jv_get_kind(j) == JV_KIND_STRING); - assert(jv_get_kind(k) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); + assert(JVP_HAS_KIND(k, JV_KIND_STRING)); const char *jstr = jv_string_value(j); const char *idxstr = jv_string_value(k); const char *p; @@ -671,8 +997,8 @@ jv jv_string_indexes(jv j, jv k) { } jv jv_string_split(jv j, jv sep) { - assert(jv_get_kind(j) == JV_KIND_STRING); - assert(jv_get_kind(sep) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); + assert(JVP_HAS_KIND(sep, JV_KIND_STRING)); const char *jstr = jv_string_value(j); const char *jend = jstr + jv_string_length_bytes(jv_copy(j)); const char *sepstr = jv_string_value(sep); @@ -703,7 +1029,7 @@ jv jv_string_split(jv j, jv sep) { } jv jv_string_explode(jv j) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); const char* i = jv_string_value(j); int len = jv_string_length_bytes(jv_copy(j)); const char* end = i + len; @@ -716,7 +1042,7 @@ jv jv_string_explode(jv j) { } jv jv_string_implode(jv j) { - assert(jv_get_kind(j) == JV_KIND_ARRAY); + assert(JVP_HAS_KIND(j, JV_KIND_ARRAY)); int len = jv_array_length(jv_copy(j)); jv s = jv_string_empty(len); int i; @@ -725,8 +1051,9 @@ jv jv_string_implode(jv j) { for (i = 0; i < len; i++) { jv n = jv_array_get(jv_copy(j), i); - assert(jv_get_kind(n) == JV_KIND_NUMBER); + assert(JVP_HAS_KIND(n, JV_KIND_NUMBER)); int nv = jv_number_value(n); + jv_free(n); if (nv > 0x10FFFF) nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER s = jv_string_append_codepoint(s, nv); @@ -737,19 +1064,19 @@ jv jv_string_implode(jv j) { } unsigned long jv_string_hash(jv j) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); uint32_t hash = jvp_string_hash(j); jv_free(j); return hash; } const char* jv_string_value(jv j) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); return jvp_string_ptr(j)->data; } jv jv_string_slice(jv j, int start, int end) { - assert(jv_get_kind(j) == JV_KIND_STRING); + assert(JVP_HAS_KIND(j, JV_KIND_STRING)); const char *s = jv_string_value(j); int len = jv_string_length_bytes(jv_copy(j)); int i; @@ -860,6 +1187,8 @@ jv jv_string_fmt(const char* fmt, ...) { * Objects (internal helpers) */ +#define JVP_FLAGS_OBJECT JVP_MAKE_FLAGS(JV_KIND_OBJECT, JVP_PAYLOAD_ALLOCATED) + struct object_slot { int next; /* next slot with same hash, for collisions */ uint32_t hash; @@ -896,22 +1225,22 @@ static jv jvp_object_new(int size) { for (int i=0; i<size*2; i++) { hashbuckets[i] = -1; } - jv r = {JV_KIND_OBJECT, 0, 0, size, {&obj->refcnt}}; + jv r = {JVP_FLAGS_OBJECT, 0, 0, size, {&obj->refcnt}}; return r; |