summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/builtin.c29
-rw-r--r--src/execute.c15
-rw-r--r--src/jq_test.c73
-rw-r--r--src/jv.c651
-rw-r--r--src/jv.h5
-rw-r--r--src/jv_aux.c39
-rw-r--r--src/jv_dtoa_tsd.c46
-rw-r--r--src/jv_dtoa_tsd.h4
-rw-r--r--src/jv_parse.c23
-rw-r--r--src/jv_print.c37
-rw-r--r--src/jv_type_private.h7
-rw-r--r--src/parser.c1029
-rw-r--r--src/parser.h16
-rw-r--r--src/parser.y24
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);
}
diff --git a/src/jv.c b/src/jv.c
index 2427b009..b2bb495a 100644
--- a/src/jv.c
+++ b/src/jv.c
@@ -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;