150823.1 · 0c93eb3379241dc4775718a9d39f54a6c4de20d6
Move source files to src/
reading declarations
@@ -19,6 +19,7 @@ jq.1 # Autotools junk .libs .deps+.dirstamp libtool *.log stamp-h1
.travis.yml
@@ -18,7 +18,7 @@ addons: - valgrind before_install:- - rm lexer.h lexer.c parser.h parser.c+ - rm src/{lexer,parser}.{c,h} install: - bundle install --gemfile=docs/Gemfile@@ -41,8 +41,9 @@ script: after_script: - | if [ -n "$COVERAGE" ]; then- rm -rf .libs usr # don't care about coverage for libjq and bison- coveralls -e lexer.c -e parser.c -e jv_dtoa.c --gcov-options '\-lp'+ rm -rf src/.libs usr # don't care about coverage for libjq and bison+ coveralls --gcov-options '\-lp' \+ -e src/lexer.c -e src/parser.c -e src/jv_dtoa.c fi after_failure:
@@ -1,13 +1,17 @@ ### C source files to be built and distributed. -LIBJQ_INCS = builtin.h bytecode.h compile.h exec_stack.h jq_parser.h \- jv_alloc.h jv_dtoa.h jv_unicode.h jv_utf8_tables.h lexer.l \- libm.h linker.h locfile.h opcode_list.h parser.y util.h--LIBJQ_SRC = builtin.c bytecode.c compile.c execute.c jq_test.c jv.c \- jv_alloc.c jv_aux.c jv_dtoa.c jv_file.c jv_parse.c jv_print.c \- jv_unicode.c linker.c locfile.c util.c ${LIBJQ_INCS}+LIBJQ_INCS = src/builtin.h src/bytecode.h src/compile.h \+ src/exec_stack.h src/jq_parser.h src/jv_alloc.h src/jv_dtoa.h \+ src/jv_unicode.h src/jv_utf8_tables.h src/lexer.l src/libm.h \+ src/linker.h src/locfile.h src/opcode_list.h src/parser.y \+ src/util.h++LIBJQ_SRC = src/builtin.c src/bytecode.c src/compile.c src/execute.c \+ src/jq_test.c src/jv.c src/jv_alloc.c src/jv_aux.c \+ src/jv_dtoa.c src/jv_file.c src/jv_parse.c src/jv_print.c \+ src/jv_unicode.c src/linker.c src/locfile.c src/util.c \+ ${LIBJQ_INCS} ### C build options @@ -21,10 +25,10 @@ ACLOCAL_AMFLAGS = -I config/m4 # While there is some autoconf macro support for lex/flex, it doesn't support # header file creation so we'll use good old make if MAINTAINER_MODE-BUILT_SOURCES = lexer.h lexer.c parser.h parser.c version.h-lexer.c: lexer.l- $(AM_V_LEX) flex -o lexer.c --header-file=lexer.h $<-lexer.h: lexer.c+BUILT_SOURCES = src/lexer.h src/lexer.c src/parser.h src/parser.c src/version.h+src/lexer.c: src/lexer.l+ $(AM_V_LEX) flex -o src/lexer.c --header-file=src/lexer.h $<+src/lexer.h: src/lexer.c else BUILT_SOURCES = .y.c:@@ -45,7 +49,7 @@ libjq_la_SOURCES = ${LIBJQ_SRC} libjq_la_LIBADD = -lm libjq_la_LDFLAGS = -export-symbols-regex '^j[qv]_' -version-info 1:4:0 -include_HEADERS = jv.h jq.h+include_HEADERS = src/jv.h src/jq.h ### Running tests under Valgrind @@ -65,7 +69,7 @@ endif if ENABLE_ERROR_INJECTION lib_LTLIBRARIES += libinject_errors.la-libinject_errors_la_SOURCES = inject_errors.c+libinject_errors_la_SOURCES = src/inject_errors.c libinject_errors_la_LIBADD = -ldl libinject_errors_la_LDFLAGS = -module endif@@ -77,13 +81,13 @@ endif .FORCE: generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed 's/.*/\#define JQ_VERSION \"&\"/'`" .remake-version-h: .FORCE- @ $(generate_ver); test "x`cat version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h-version.h: .remake-version-h+ @ $(generate_ver); test "x`cat src/version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h+src/version.h: .remake-version-h $(AM_V_GEN) $(generate_ver); echo "$$ver" > $@-main.c: version.h+src/main.c: src/version.h bin_PROGRAMS = jq-jq_SOURCES = main.c version.h+jq_SOURCES = src/main.c src/version.h jq_LDFLAGS = -static-libtool-libs jq_LDADD = libjq.la -lm @@ -146,8 +150,8 @@ DOC_FILES = docs/content docs/public docs/templates docs/site.yml \ docs/default_manpage.md jq.1.default EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \- jq.1.prebuilt jq.spec lexer.c lexer.h parser.c parser.h \- scripts/version tests/jq.test tests/modules/.jq \+ jq.1.prebuilt jq.spec src/lexer.c src/lexer.h src/parser.c \+ src/parser.h scripts/version tests/jq.test tests/modules/.jq \ tests/modules/a.jq tests/modules/b/b.jq tests/modules/c/c.jq \ tests/modules/c/d.jq tests/modules/data.json \ tests/modules/lib/jq/e/e.jq tests/modules/lib/jq/f.jq \@@ -185,9 +189,9 @@ if ENABLE_DOCS endif clean-local-gcov:- rm -f *.gcno *.gcda *.gcov+ rm -f src/*.gcno src/*.gcda src/*.gcov clean-local: clean-local-docs clean-local-gcov- rm -f version.h .remake-version-h+ rm -f src/version.h .remake-version-h .PHONY: clean-local-docs clean-local-gcov
builtin.c
@@ -1,1701 +0,0 @@-#define _BSD_SOURCE-#define _GNU_SOURCE-#define _XOPEN_SOURCE-#include <sys/time.h>-#include <stdlib.h>-#include <stddef.h>-#ifdef HAVE_ALLOCA_H-# include <alloca.h>-#elif !defined alloca-# ifdef __GNUC__-# define alloca __builtin_alloca-# elif defined _MSC_VER-# include <malloc.h>-# define alloca _alloca-# elif !defined HAVE_ALLOCA-# ifdef __cplusplus-extern "C"-# endif-void *alloca (size_t);-# endif-#endif-#include <assert.h>-#include <ctype.h>-#include <limits.h>-#include <math.h>-#ifdef HAVE_ONIGURUMA-#include <oniguruma.h>-#endif-#include <string.h>-#include <time.h>-#include "builtin.h"-#include "compile.h"-#include "jq_parser.h"-#include "bytecode.h"-#include "linker.h"-#include "locfile.h"-#include "jv_unicode.h"---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 jv type_error2(jv bad1, jv bad2, const char* msg) {- char errbuf1[15],errbuf2[15];- jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s",- jv_kind_name(jv_get_kind(bad1)),- jv_dump_string_trunc(jv_copy(bad1), errbuf1, sizeof(errbuf1)),- jv_kind_name(jv_get_kind(bad2)),- jv_dump_string_trunc(jv_copy(bad2), errbuf2, sizeof(errbuf2)),- msg));- jv_free(bad1);- jv_free(bad2);- return err;-}--static jv f_plus(jq_state *jq, jv input, jv a, jv b) {- jv_free(input);- if (jv_get_kind(a) == JV_KIND_NULL) {- jv_free(a);- return b;- } else if (jv_get_kind(b) == JV_KIND_NULL) {- 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_number_value(b));- } 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) {- return jv_array_concat(a, b);- } else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) {- return jv_object_merge(a, b);- } else {- return type_error2(a, b, "cannot be added");- }-}--#define LIBM_DD(name) \-static jv f_ ## name(jq_state *jq, jv input) { \- if (jv_get_kind(input) != JV_KIND_NUMBER) { \- return type_error(input, "number required"); \- } \- jv ret = jv_number(name(jv_number_value(input))); \- jv_free(input); \- return ret; \-}-#define LIBM_DD_NO(name)--#define LIBM_DDD(name) \-static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \- if (jv_get_kind(a) != JV_KIND_NUMBER || jv_get_kind(b) != JV_KIND_NUMBER) \- return type_error(input, "number required"); \- jv_free(input); \- jv ret = jv_number(name(jv_number_value(a), jv_number_value(b))); \- jv_free(a); \- jv_free(b); \- return ret; \-}-#define LIBM_DDD_NO(name)-#include "libm.h"-#undef LIBM_DDD_NO-#undef LIBM_DD_NO-#undef LIBM_DDD-#undef LIBM_DD--static jv f_negate(jq_state *jq, jv input) {- if (jv_get_kind(input) != JV_KIND_NUMBER) {- return type_error(input, "cannot be negated");- }- jv ret = jv_number(-jv_number_value(input));- jv_free(input);- 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 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_ltrimstr(jq_state *jq, jv input, jv left) {- if (jv_get_kind(f_startswith(jq, 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 jv f_rtrimstr(jq_state *jq, jv input, jv right) {- if (jv_get_kind(f_endswith(jq, 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 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));- } 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);- }- jv_free(a);- jv_free(b);- return out;- } else {- return type_error2(a, b, "cannot be subtracted");- }-}--static jv f_multiply(jq_state *jq, jv input, jv a, jv b) {- jv_kind ak = jv_get_kind(a);- 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));- } else if ((ak == JV_KIND_STRING && bk == JV_KIND_NUMBER) ||- (ak == JV_KIND_NUMBER && bk == JV_KIND_STRING)) {- jv str = a;- jv num = b;- if (ak == JV_KIND_NUMBER) {- str = b;- num = a;- }- int n;- size_t alen = jv_string_length_bytes(jv_copy(str));- jv res = str;-- for (n = jv_number_value(num) - 1; n > 0; n--)- res = jv_string_append_buf(res, jv_string_value(str), alen);-- jv_free(num);- if (n < 0) {- jv_free(str);- return jv_null();- }- return res;- } else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) {- return jv_object_merge_recursive(a, b);- } else {- return type_error2(a, b, "cannot be multiplied");- }-}--static jv f_divide(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) {- 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));- } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {- return jv_string_split(a, b);- } else {- return type_error2(a, b, "cannot be divided");- }-}--static jv f_mod(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) {- 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));- } else {- return type_error2(a, b, "cannot be divided (remainder)");- }-}--static jv f_equal(jq_state *jq, jv input, jv a, jv b) {- jv_free(input);- return jv_bool(jv_equal(a, b));-}--static jv f_notequal(jq_state *jq, jv input, jv a, jv b) {- jv_free(input);- return jv_bool(!jv_equal(a, b));-}--enum cmp_op {- CMP_OP_LESS,- CMP_OP_GREATER,- CMP_OP_LESSEQ,- CMP_OP_GREATEREQ-};--static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {- jv_free(input);- int r = jv_cmp(a, b);- return jv_bool((op == CMP_OP_LESS && r < 0) ||- (op == CMP_OP_LESSEQ && r <= 0) ||- (op == CMP_OP_GREATEREQ && r >= 0) ||- (op == CMP_OP_GREATER && r > 0));-}--static jv f_less(jq_state *jq, jv input, jv a, jv b) {- return order_cmp(input, a, b, CMP_OP_LESS);-}--static jv f_greater(jq_state *jq, jv input, jv a, jv b) {- return order_cmp(input, a, b, CMP_OP_GREATER);-}--static jv f_lesseq(jq_state *jq, jv input, jv a, jv b) {- return order_cmp(input, a, b, CMP_OP_LESSEQ);-}--static jv f_greatereq(jq_state *jq, jv input, jv a, jv b) {- return order_cmp(input, a, b, CMP_OP_GREATEREQ);-}--static jv f_contains(jq_state *jq, jv a, jv b) {- if (jv_get_kind(a) == jv_get_kind(b)) {- return jv_bool(jv_contains(a, b));- } else {- return type_error2(a, b, "cannot have their containment checked");- }-}--static jv f_dump(jq_state *jq, jv input) {- return jv_dump_string(input, 0);-}--static jv f_json_parse(jq_state *jq, jv input) {- if (jv_get_kind(input) != JV_KIND_STRING)- return type_error(input, "only strings can be parsed");- jv res = jv_parse_sized(jv_string_value(input),- jv_string_length_bytes(jv_copy(input)));- jv_free(input);- return res;-}--static jv f_tonumber(jq_state *jq, jv input) {- if (jv_get_kind(input) == JV_KIND_NUMBER) {- return input;- }- if (jv_get_kind(input) == JV_KIND_STRING) {- jv parsed = jv_parse(jv_string_value(input));- if (!jv_is_valid(parsed) || jv_get_kind(parsed) == JV_KIND_NUMBER) {- jv_free(input);- return parsed;- }- }- return type_error(input, "cannot be parsed as a number");-}--static jv f_length(jq_state *jq, jv input) {- if (jv_get_kind(input) == JV_KIND_ARRAY) {- return jv_number(jv_array_length(input));- } else if (jv_get_kind(input) == JV_KIND_OBJECT) {- return jv_number(jv_object_length(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)));- } else if (jv_get_kind(input) == JV_KIND_NULL) {- jv_free(input);- return jv_number(0);- } else {- return type_error(input, "has no length");- }-}--static jv f_tostring(jq_state *jq, jv input) {- if (jv_get_kind(input) == JV_KIND_STRING) {- return input;- } else {- return jv_dump_string(input, 0);- }-}--#define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"--static jv escape_string(jv input, const char* escapings) {-- assert(jv_get_kind(input) == JV_KIND_STRING);- const char* lookup[128] = {0};- const char* p = escapings;- lookup[0] = "\\0";- while (*p) {- lookup[(int)*p] = p+1;- p++;- p += strlen(p);- p++;- }-- jv ret = jv_string("");- const char* i = jv_string_value(input);- const char* end = i + jv_string_length_bytes(jv_copy(input));- const char* cstart;- int c = 0;- while ((i = jvp_utf8_next((cstart = i), end, &c))) {- if (c < 128 && lookup[c]) {- ret = jv_string_append_str(ret, lookup[c]);- } else {- ret = jv_string_append_buf(ret, cstart, i - cstart);- }- }- jv_free(input);- return ret;--}--static jv f_format(jq_state *jq, jv input, jv fmt) {- if (jv_get_kind(fmt) != JV_KIND_STRING) {- jv_free(input);- return type_error(fmt, "is not a valid format");- }- const char* fmt_s = jv_string_value(fmt);- if (!strcmp(fmt_s, "json")) {- jv_free(fmt);- return jv_dump_string(input, 0);- } else if (!strcmp(fmt_s, "text")) {- jv_free(fmt);- return f_tostring(jq, input);- } else if (!strcmp(fmt_s, "csv") || !strcmp(fmt_s, "tsv")) {- const char *quotes, *sep, *escapings;- const char *msg;- if (!strcmp(fmt_s, "csv")) {- msg = "cannot be csv-formatted, only array";- quotes = "\"";- sep = ",";- escapings = "\"\"\"\0";- } else {- msg = "cannot be tsv-formatted, only array";- assert(!strcmp(fmt_s, "tsv"));- quotes = "";- sep = "\t";- escapings = "\t\\t\0\r\\r\0\n\\n\0\\\\\\\0";- }- jv_free(fmt);- if (jv_get_kind(input) != JV_KIND_ARRAY)- return type_error(input, msg);- jv line = jv_string("");- jv_array_foreach(input, i, x) {- if (i) line = jv_string_append_str(line, sep);- switch (jv_get_kind(x)) {- case JV_KIND_NULL:- /* null rendered as empty string */- jv_free(x);- break;- case JV_KIND_TRUE:- case JV_KIND_FALSE:- line = jv_string_concat(line, jv_dump_string(x, 0));- break;- case JV_KIND_NUMBER:- if (jv_number_value(x) != jv_number_value(x)) {- /* NaN, render as empty string */- jv_free(x);- } else {- line = jv_string_concat(line, jv_dump_string(x, 0));- }- break;- case JV_KIND_STRING: {- line = jv_string_append_str(line, quotes);- line = jv_string_concat(line, escape_string(x, escapings));- line = jv_string_append_str(line, quotes);- break;- }- default:- jv_free(input);- jv_free(line);- return type_error(x, "is not valid in a csv row");- }- }- jv_free(input);- return line;- } else if (!strcmp(fmt_s, "html")) {- jv_free(fmt);- return escape_string(f_tostring(jq, input), "&&\0<<\0>>\0''\0\""\0");- } else if (!strcmp(fmt_s, "uri")) {- jv_free(fmt);- input = f_tostring(jq, input);-- int unreserved[128] = {0};- const char* p = CHARS_ALPHANUM "-_.!~*'()";- while (*p) unreserved[(int)*p++] = 1;-- jv line = jv_string("");- const char* s = jv_string_value(input);- for (int i=0; i<jv_string_length_bytes(jv_copy(input)); i++) {- unsigned ch = (unsigned)(unsigned char)*s;- if (ch < 128 && unreserved[ch]) {- line = jv_string_append_buf(line, s, 1);- } else {- line = jv_string_concat(line, jv_string_fmt("%%%02X", ch));- }- s++;- }- jv_free(input);- return line;- } else if (!strcmp(fmt_s, "sh")) {- jv_free(fmt);- if (jv_get_kind(input) != JV_KIND_ARRAY)- input = jv_array_set(jv_array(), 0, input);- jv line = jv_string("");- jv_array_foreach(input, i, x) {- if (i) line = jv_string_append_str(line, " ");- switch (jv_get_kind(x)) {- case JV_KIND_NULL:- case JV_KIND_TRUE:- case JV_KIND_FALSE:- case JV_KIND_NUMBER:- line = jv_string_concat(line, jv_dump_string(x, 0));- break;-- case JV_KIND_STRING: {- line = jv_string_append_str(line, "'");- line = jv_string_concat(line, escape_string(x, "''\\''\0"));- line = jv_string_append_str(line, "'");- break;- }-- default:- jv_free(input);- jv_free(line);- return type_error(x, "can not be escaped for shell");- }- }- jv_free(input);- return line;- } else if (!strcmp(fmt_s, "base64")) {- jv_free(fmt);- input = f_tostring(jq, input);- jv line = jv_string("");- const char b64[64 + 1] = CHARS_ALPHANUM "+/";- const unsigned char* data = (const unsigned char*)jv_string_value(input);- int len = jv_string_length_bytes(jv_copy(input));- for (int i=0; i<len; i+=3) {- uint32_t code = 0;- int n = len - i >= 3 ? 3 : len-i;- for (int j=0; j<3; j++) {- code <<= 8;- code |= j < n ? (unsigned)data[i+j] : 0;- }- char buf[4];- for (int j=0; j<4; j++) {- buf[j] = b64[(code >> (18 - j*6)) & 0x3f];- }- if (n < 3) buf[3] = '=';- if (n < 2) buf[2] = '=';- line = jv_string_append_buf(line, buf, sizeof(buf));- }- jv_free(input);- return line;- } else {- jv_free(input);- return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format")));- }-}--static jv f_keys(jq_state *jq, jv input) {- if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {- return jv_keys(input);- } else {- return type_error(input, "has no keys");- }-}--static jv f_keys_unsorted(jq_state *jq, jv input) {- if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {- return jv_keys_unsorted(input);- } else {- return type_error(input, "has no keys");- }-}--static jv f_sort(jq_state *jq, jv input){- if (jv_get_kind(input) == JV_KIND_ARRAY) {- return jv_sort(input, jv_copy(input));- } else {- return type_error(input, "cannot be sorted, as it is not an array");- }-}--static jv f_sort_by_impl(jq_state *jq, jv input, jv keys) {- if (jv_get_kind(input) == JV_KIND_ARRAY &&- jv_get_kind(keys) == JV_KIND_ARRAY &&- jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {- return jv_sort(input, keys);- } else {- return type_error2(input, keys, "cannot be sorted, as they are not both arrays");- }-}--static jv f_group_by_impl(jq_state *jq, jv input, jv keys) {- if (jv_get_kind(input) == JV_KIND_ARRAY &&- jv_get_kind(keys) == JV_KIND_ARRAY &&- jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {- return jv_group(input, keys);- } else {- return type_error2(input, keys, "cannot be sorted, as they are not both arrays");- }-}--#ifdef HAVE_ONIGURUMA-static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups,- int *groups, regex_t *reg, void *arg) {- jv captures = *(jv*)arg;- for (int i = 0; i < ngroups; ++i) {- jv cap = jv_array_get(jv_copy(captures),groups[i]-1);- if (jv_get_kind(cap) == JV_KIND_OBJECT) {- cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name));- captures = jv_array_set(captures,groups[i]-1,cap);- } else {- jv_free(cap);- }- }- *(jv *)arg = captures;- return 0;-}--static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {- int test = jv_equal(testmode, jv_true());- jv result;- int onigret;- int global = 0;- regex_t *reg;- OnigErrorInfo einfo;- OnigRegion* region;-- if (jv_get_kind(input) != JV_KIND_STRING) {- jv_free(regex);- jv_free(modifiers);- return type_error(input, "cannot be matched, as it is not a string");- }-- if (jv_get_kind(regex) != JV_KIND_STRING) {- jv_free(input);- jv_free(modifiers);- return type_error(regex, "is not a string");- }-- OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP;-- if (jv_get_kind(modifiers) == JV_KIND_STRING) {- jv modarray = jv_string_explode(jv_copy(modifiers));- jv_array_foreach(modarray, i, mod) {- switch ((int)jv_number_value(mod)) {- case 'g':- global = 1;- break;- case 'i':- options |= ONIG_OPTION_IGNORECASE;- break;- case 'x':- options |= ONIG_OPTION_EXTEND;- break;- case 'm':- options |= ONIG_OPTION_MULTILINE;- break;- case 's':- options |= ONIG_OPTION_SINGLELINE;- break;- case 'p':- options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE;- break;- case 'l':- options |= ONIG_OPTION_FIND_LONGEST;- break;- case 'n':- options |= ONIG_OPTION_FIND_NOT_EMPTY;- break;- default:- jv_free(input);- jv_free(regex);- jv_free(modarray);- return jv_invalid_with_msg(jv_string_concat(modifiers,- jv_string(" is not a valid modifier string")));- }- }- jv_free(modarray);- } else if (jv_get_kind(modifiers) != JV_KIND_NULL) {- // If it isn't a string or null, then it is the wrong type...- jv_free(input);- jv_free(regex);- return type_error(modifiers, "is not a string");- }-- jv_free(modifiers);-- onigret = onig_new(®, (const UChar*)jv_string_value(regex),- (const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))),- options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo);- if (onigret != ONIG_NORMAL) {- UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];- onig_error_code_to_str(ebuf, onigret, &einfo);- jv_free(input);- jv_free(regex);- return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),- jv_string((char*)ebuf)));- }- result = test ? jv_false() : jv_array();- const char *input_string = jv_string_value(input);- const UChar* start = (const UChar*)jv_string_value(input);- const unsigned long length = jv_string_length_bytes(jv_copy(input));- const UChar* end = start + length;- region = onig_region_new();- do {- onigret = onig_search(reg,- (const UChar*)jv_string_value(input), end, /* string boundaries */- start, end, /* search boundaries */- region, ONIG_OPTION_NONE);- if (onigret >= 0) {- if (test) {- result = jv_true();- break;- }-- // Zero-width match- if (region->end[0] == region->beg[0]) {- unsigned long idx;- const char *fr = (const char*)input_string;- for (idx = 0; fr != input_string+region->beg[0]; idx++) {- fr += jvp_utf8_decode_length(*fr);- }- jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));- match = jv_object_set(match, jv_string("length"), jv_number(0));- match = jv_object_set(match, jv_string("string"), jv_string(""));- match = jv_object_set(match, jv_string("captures"), jv_array());- result = jv_array_append(result, match);- start += 1;- continue;- }-- unsigned long idx;- unsigned long len;- const char *fr = (const char*)input_string;-- for (idx = len = 0; fr < input_string+region->end[0]; len++) {- if (fr == input_string+region->beg[0]) idx = len, len=0;- fr += jvp_utf8_decode_length(*fr);- }-- jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));-- unsigned long blen = region->end[0]-region->beg[0];- match = jv_object_set(match, jv_string("length"), jv_number(len));- match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen));- jv captures = jv_array();- for (int i = 1; i < region->num_regs; ++i) {- // Empty capture.- if (region->beg[i] == region->end[i]) {- // Didn't match.- jv cap;- if (region->beg[i] == -1) {- cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1));- cap = jv_object_set(cap, jv_string("string"), jv_null());- } else {- fr = input_string;- for (idx = 0; fr != input_string+region->beg[i]; idx++) {- fr += jvp_utf8_decode_length(*fr);- }- cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));- cap = jv_object_set(cap, jv_string("string"), jv_string(""));- }- cap = jv_object_set(cap, jv_string("length"), jv_number(0));- cap = jv_object_set(cap, jv_string("name"), jv_null());- captures = jv_array_append(captures, cap);- continue;- }- fr = input_string;- for (idx = len = 0; fr != input_string+region->end[i]; len++) {- if (fr == input_string+region->beg[i]) idx = len, len=0;- fr += jvp_utf8_decode_length(*fr);- }-- blen = region->end[i]-region->beg[i];- jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));- cap = jv_object_set(cap, jv_string("length"), jv_number(len));- cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen));- cap = jv_object_set(cap, jv_string("name"), jv_null());- captures = jv_array_append(captures,cap);- }- onig_foreach_name(reg,f_match_name_iter,&captures);- match = jv_object_set(match, jv_string("captures"), captures);- result = jv_array_append(result, match);- start = (const UChar*)(input_string+region->end[0]);- onig_region_free(region,0);- } else if (onigret == ONIG_MISMATCH) {- break;- } else { /* Error */- UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];- onig_error_code_to_str(ebuf, onigret, einfo);- jv_free(result);- result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),- jv_string((char*)ebuf)));- break;- }- } while (global && start != end);- onig_region_free(region,1);- region = NULL;- if (region)- onig_region_free(region,1);- onig_free(reg);- jv_free(input);- jv_free(regex);- return result;-}-#else /* ! HAVE_ONIGURUMA */-static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {- return jv_invalid_with_msg(jv_string("jq was compiled without ONIGURUMA regex libary. match/test/sub and related functions are not available."));-}-#endif /* HAVE_ONIGURUMA */--static jv minmax_by(jv values, jv keys, int is_min) {- if (jv_get_kind(values) != JV_KIND_ARRAY)- return type_error2(values, keys, "cannot be iterated over");- if (jv_get_kind(keys) != JV_KIND_ARRAY)- return type_error2(values, keys, "cannot be iterated over");- if (jv_array_length(jv_copy(values)) != jv_array_length(jv_copy(keys)))- return type_error2(values, keys, "have wrong length");-- if (jv_array_length(jv_copy(values)) == 0) {- jv_free(values);- jv_free(keys);- return jv_null();- }- jv ret = jv_array_get(jv_copy(values), 0);- jv retkey = jv_array_get(jv_copy(keys), 0);- for (int i=1; i<jv_array_length(jv_copy(values)); i++) {- jv item = jv_array_get(jv_copy(keys), i);- int cmp = jv_cmp(jv_copy(item), jv_copy(retkey));- if ((cmp < 0) == (is_min == 1)) {- jv_free(retkey);- retkey = item;- jv_free(ret);- ret = jv_array_get(jv_copy(values), i);- } else {- jv_free(item);- }- }- jv_free(values);- jv_free(keys);- jv_free(retkey);- return ret;-}--static jv f_min(jq_state *jq, jv x) {- return minmax_by(x, jv_copy(x), 1);-}--static jv f_max(jq_state *jq, jv x) {- return minmax_by(x, jv_copy(x), 0);-}--static jv f_min_by_impl(jq_state *jq, jv x, jv y) {- return minmax_by(x, y, 1);-}--static jv f_max_by_impl(jq_state *jq, jv x, jv y) {- return minmax_by(x, y, 0);-}---static jv f_type(jq_state *jq, jv input) {- jv out = jv_string(jv_kind_name(jv_get_kind(input)));- jv_free(input);- return out;-}--static jv f_isinfinite(jq_state *jq, jv input) {- jv_kind k = jv_get_kind(input);- if (k != JV_KIND_NUMBER) {- jv_free(input);- return jv_false();- }- double n = jv_number_value(input);- jv_free(input);- return isinf(n) ? jv_true() : jv_false();-}--static jv f_isnan(jq_state *jq, jv input) {- jv_kind k = jv_get_kind(input);- if (k != JV_KIND_NUMBER) {- jv_free(input);- return jv_false();- }- double n = jv_number_value(input);- jv_free(input);- return isnan(n) ? jv_true() : jv_false();-}--static jv f_isnormal(jq_state *jq, jv input) {- jv_kind k = jv_get_kind(input);- if (k != JV_KIND_NUMBER) {- jv_free(input);- return jv_false();- }- double n = jv_number_value(input);- jv_free(input);- return isnormal(n) ? jv_true() : jv_false();-}--static jv f_infinite(jq_state *jq, jv input) {- jv_free(input);- return jv_number(INFINITY);-}--static jv f_nan(jq_state *jq, jv input) {- jv_free(input);- return jv_number(NAN);-}--static jv f_error(jq_state *jq, jv input, jv msg) {- jv_free(input);- return jv_invalid_with_msg(msg);-}--// FIXME Should autoconf check for this!-#ifndef WIN32-extern const char **environ;-#endif--static jv f_env(jq_state *jq, jv input) {- jv_free(input);- jv env = jv_object();- const char *var, *val;- for (const char **e = environ; *e != NULL; e++) {- var = e[0];- val = strchr(e[0], '=');- if (val == NULL)- env = jv_object_set(env, jv_string(var), jv_null());- else if (var - val < INT_MAX)- env = jv_object_set(env, jv_string_sized(var, val - var), jv_string(val + 1));- }- return env;-}--static jv f_get_search_list(jq_state *jq, jv input) {- jv_free(input);- return jq_get_lib_dirs(jq);-}--static jv f_get_prog_origin(jq_state *jq, jv input) {- jv_free(input);- return jq_get_prog_origin(jq);-}--static jv f_get_jq_origin(jq_state *jq, jv input) {- jv_free(input);- return jq_get_jq_origin(jq);-}--static jv f_string_split(jq_state *jq, jv a, jv b) {- if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {- jv_free(a);- jv_free(b);- return jv_invalid_with_msg(jv_string("split input and separator must be strings"));- }- return jv_string_split(a, b);-}--static jv f_string_explode(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_STRING) {- jv_free(a);- return jv_invalid_with_msg(jv_string("explode input must be a string"));- }- return jv_string_explode(a);-}--static jv f_string_indexes(jq_state *jq, jv a, jv b) {- return jv_string_indexes(a, b);-}--static jv f_string_implode(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_ARRAY) {- jv_free(a);- return jv_invalid_with_msg(jv_string("implode input must be an array"));- }- return jv_string_implode(a);-}--static jv f_setpath(jq_state *jq, jv a, jv b, jv c) { return jv_setpath(a, b, c); }-static jv f_getpath(jq_state *jq, jv a, jv b) { return jv_getpath(a, b); }-static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); }-static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); }--static jv f_modulemeta(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_STRING) {- jv_free(a);- return jv_invalid_with_msg(jv_string("modulemeta input module name must be a string"));- }- return load_module_meta(jq, a);-}--static jv f_input(jq_state *jq, jv input) {- jv_free(input);- jq_input_cb cb;- void *data;- jq_get_input_cb(jq, &cb, &data);- if (cb == NULL)- return jv_invalid_with_msg(jv_string("break"));- jv v = cb(jq, data);- if (jv_is_valid(v) || jv_invalid_has_msg(jv_copy(v)))- return v;- return jv_invalid_with_msg(jv_string("break"));-}--static jv f_debug(jq_state *jq, jv input) {- jq_msg_cb cb;- void *data;- jq_get_debug_cb(jq, &cb, &data);- if (cb != NULL)- cb(data, jv_copy(input));- return input;-}--static jv f_stderr(jq_state *jq, jv input) {- jv_dumpf(jv_copy(input), stderr, 0);- fprintf(stderr, "\n");- return input;-}--static jv tm2jv(struct tm *tm) {- return JV_ARRAY(jv_number(tm->tm_year + 1900),- jv_number(tm->tm_mon),- jv_number(tm->tm_mday),- jv_number(tm->tm_hour),- jv_number(tm->tm_min),- jv_number(tm->tm_sec),- jv_number(tm->tm_wday),- jv_number(tm->tm_yday));-}--/*- * mktime() has side-effects and anyways, returns time in the local- * timezone, not UTC. We want timegm(), which isn't standard.- *- * To make things worse, mktime() tells you what the timezone- * adjustment is, but you have to #define _BSD_SOURCE to get this- * field of struct tm on some systems.- *- * This is all to blame on POSIX, of course.- *- * Our wrapper tries to use timegm() if available, or mktime() and- * correct for its side-effects if possible.- *- * Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.- */-static time_t my_mktime(struct tm *tm) {-#ifdef HAVE_TIMEGM- return timegm(tm);-#else /* HAVE_TIMEGM */- time_t t = mktime(tm);- if (t == (time_t)-1)- return t;-#ifdef HAVE_TM_TM_GMT_OFF- return t + tm.tm_gmtoff;-#elif defined(HAVE_TM_TM_GMT_OFF)- return t + tm.__tm_gmtoff;-#else- return (time_t)-2; /* Not supported */-#endif-#endif /* !HAVE_TIMEGM */-}--#ifdef HAVE_STRPTIME-static jv f_strptime(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("strptime/1 requires string inputs and arguments"));-- struct tm tm;- memset(&tm, 0, sizeof(tm));- const char *input = jv_string_value(a);- const char *fmt = jv_string_value(b);- const char *end = strptime(input, fmt, &tm);-- if (end == NULL || (*end != '\0' && !isspace(*end))) {- jv e = jv_invalid_with_msg(jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));- jv_free(a);- jv_free(b);- return e;- }- jv_free(b);- if (tm.tm_wday == 0 && tm.tm_yday == 0 && my_mktime(&tm) == (time_t)-2) {- jv_free(a);- return jv_invalid_with_msg(jv_string("strptime/1 not supported on this platform"));- }- jv r = tm2jv(&tm);- if (*end != '\0')- r = jv_array_append(r, jv_string(end));- jv_free(a); // must come after `*end` because `end` is a pointer into `a`'s string- return r;-}-#else-static jv f_strptime(jq_state *jq, jv a, jv b) {- jv_free(a);- jv_free(b);- return jv_invalid_with_msg(jv_string("strptime/1 not implemented on this platform"));-}-#endif--#define TO_TM_FIELD(t, j, i) \- do { \- jv n = jv_array_get(jv_copy(j), (i)); \- if (jv_get_kind(n) != (JV_KIND_NUMBER)) \- return 0; \- t = jv_number_value(n); \- jv_free(n); \- } while (0)--static int jv2tm(jv a, struct tm *tm) {- memset(tm, 0, sizeof(*tm));- TO_TM_FIELD(tm->tm_year, a, 0);- tm->tm_year -= 1900;- TO_TM_FIELD(tm->tm_mon, a, 1);- TO_TM_FIELD(tm->tm_mday, a, 2);- TO_TM_FIELD(tm->tm_hour, a, 3);- TO_TM_FIELD(tm->tm_min, a, 4);- TO_TM_FIELD(tm->tm_sec, a, 5);- TO_TM_FIELD(tm->tm_wday, a, 6);- TO_TM_FIELD(tm->tm_yday, a, 7);- jv_free(a);-- // We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.- // Setting tm_isdst to 0 is done by the memset.- // tm->tm_isdst = 0;-- // The standard permits the tm structure to contain additional members. We- // hope it is okay to initialize them to zero, because the standard does not- // provide an alternative.-- return 1;-}--#undef TO_TM_FIELD--static jv f_mktime(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_ARRAY)- return jv_invalid_with_msg(jv_string("mktime requires array inputs"));- if (jv_array_length(jv_copy(a)) < 6)- return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));- struct tm tm;- if (!jv2tm(a, &tm))- return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));- time_t t = my_mktime(&tm);- if (t == (time_t)-1)- return jv_invalid_with_msg(jv_string("invalid gmtime representation"));- if (t == (time_t)-2)- return jv_invalid_with_msg(jv_string("mktime not supported on this platform"));- return jv_number(t);-}--#ifdef HAVE_GMTIME_R-static jv f_gmtime(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_NUMBER)- return jv_invalid_with_msg(jv_string("gmtime() requires numeric inputs"));- struct tm tm, *tmp;- memset(&tm, 0, sizeof(tm));- double fsecs = jv_number_value(a);- time_t secs = fsecs;- jv_free(a);- tmp = gmtime_r(&secs, &tm);- if (tmp == NULL)- return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));- a = tm2jv(tmp);- return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));-}-#elif defined HAVE_GMTIME-static jv f_gmtime(jq_state *jq, jv a) {- if (jv_get_kind(a) != JV_KIND_NUMBER)- return jv_invalid_with_msg(jv_string("gmtime requires numeric inputs"));- struct tm tm, *tmp;- memset(&tm, 0, sizeof(tm));- double fsecs = jv_number_value(a);- time_t secs = fsecs;- jv_free(a);- tmp = gmtime(&secs);- if (tmp == NULL)- return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));- a = tm2jv(tmp);- return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));-}-#else-static jv f_gmtime(jq_state *jq, jv a) {- jv_free(a);- return jv_invalid_with_msg(jv_string("gmtime not implemented on this platform"));-}-#endif--#ifdef HAVE_STRFTIME-static jv f_strftime(jq_state *jq, jv a, jv b) {- if (jv_get_kind(a) == JV_KIND_NUMBER) {- a = f_gmtime(jq, a);- } else if (jv_get_kind(a) != JV_KIND_ARRAY) {- return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs"));- }- struct tm tm;- if (!jv2tm(a, &tm))- return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs")); \- const char *fmt = jv_string_value(b);- size_t alloced = strlen(fmt) + 100;- char *buf = alloca(alloced);- size_t n = strftime(buf, alloced, fmt, &tm);- jv_free(b);- /* POSIX doesn't provide errno values for strftime() failures; weird */- if (n == 0 || n > alloced)- return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));- return jv_string(buf);-}-#else-static jv f_strftime(jq_state *jq, jv a) {- jv_free(a);- jv_free(b);- return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform"));-}-#endif--#ifdef HAVE_GETTIMEOFDAY-static jv f_now(jq_state *jq, jv a) {- jv_free(a);- struct timeval tv;- if (gettimeofday(&tv, NULL) == -1)- return jv_number(time(NULL));- return jv_number(tv.tv_sec + tv.tv_usec / 1000000.0);-}-#else-static jv f_now(jq_state *jq, jv a) {- jv_free(a);- return jv_number(time(NULL));-}-#endif--static jv f_current_filename(jq_state *jq) {- jv r = jq_util_input_get_current_filename(jq);- if (jv_is_valid(r))- return r;- jv_free(r);- return jv_null();-}-static jv f_current_line(jq_state *jq) {- return jq_util_input_get_current_line(jq);-}--#define LIBM_DD(name) \- {(cfunction_ptr)f_ ## name, "_" #name, 1},-#define LIBM_DD_NO(name)--#define LIBM_DDD(name) \- {(cfunction_ptr)f_ ## name, "_" #name, 3},-#define LIBM_DDD_NO(name)--static const struct cfunction function_list[] = {-#include "libm.h"- {(cfunction_ptr)f_plus, "_plus", 3},- {(cfunction_ptr)f_negate, "_negate", 1},- {(cfunction_ptr)f_minus, "_minus", 3},- {(cfunction_ptr)f_multiply, "_multiply", 3},- {(cfunction_ptr)f_divide, "_divide", 3},- {(cfunction_ptr)f_mod, "_mod", 3},- {(cfunction_ptr)f_dump, "tojson", 1},- {(cfunction_ptr)f_json_parse, "fromjson", 1},- {(cfunction_ptr)f_tonumber, "tonumber", 1},- {(cfunction_ptr)f_tostring, "tostring", 1},- {(cfunction_ptr)f_keys, "keys", 1},- {(cfunction_ptr)f_keys_unsorted, "keys_unsorted", 1},- {(cfunction_ptr)f_startswith, "startswith", 2},- {(cfunction_ptr)f_endswith, "endswith", 2},- {(cfunction_ptr)f_ltrimstr, "ltrimstr", 2},- {(cfunction_ptr)f_rtrimstr, "rtrimstr", 2},- {(cfunction_ptr)f_string_split, "split", 2},- {(cfunction_ptr)f_string_explode, "explode", 1},- {(cfunction_ptr)f_string_implode, "implode", 1},- {(cfunction_ptr)f_string_indexes, "_strindices", 2},- {(cfunction_ptr)f_setpath, "setpath", 3}, // FIXME typechecking- {(cfunction_ptr)f_getpath, "getpath", 2},- {(cfunction_ptr)f_delpaths, "delpaths", 2},- {(cfunction_ptr)f_has, "has", 2},- {(cfunction_ptr)f_equal, "_equal", 3},- {(cfunction_ptr)f_notequal, "_notequal", 3},- {(cfunction_ptr)f_less, "_less", 3},- {(cfunction_ptr)f_greater, "_greater", 3},- {(cfunction_ptr)f_lesseq, "_lesseq", 3},- {(cfunction_ptr)f_greatereq, "_greatereq", 3},- {(cfunction_ptr)f_contains, "contains", 2},- {(cfunction_ptr)f_length, "length", 1},- {(cfunction_ptr)f_type, "type", 1},- {(cfunction_ptr)f_isinfinite, "isinfinite", 1},- {(cfunction_ptr)f_isnan, "isnan", 1},- {(cfunction_ptr)f_isnormal, "isnormal", 1},- {(cfunction_ptr)f_infinite, "infinite", 1},- {(cfunction_ptr)f_nan, "nan", 1},- {(cfunction_ptr)f_sort, "sort", 1},- {(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},- {(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},- {(cfunction_ptr)f_min, "min", 1},- {(cfunction_ptr)f_max, "max", 1},- {(cfunction_ptr)f_min_by_impl, "_min_by_impl", 2},- {(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},- {(cfunction_ptr)f_error, "error", 2},- {(cfunction_ptr)f_format, "format", 2},- {(cfunction_ptr)f_env, "env", 1},- {(cfunction_ptr)f_get_search_list, "get_search_list", 1},- {(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},- {(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},- {(cfunction_ptr)f_match, "_match_impl", 4},- {(cfunction_ptr)f_modulemeta, "modulemeta", 1},- {(cfunction_ptr)f_input, "_input", 1},- {(cfunction_ptr)f_debug, "debug", 1},- {(cfunction_ptr)f_stderr, "stderr", 1},- {(cfunction_ptr)f_strptime, "strptime", 2},- {(cfunction_ptr)f_strftime, "strftime", 2},- {(cfunction_ptr)f_mktime, "mktime", 1},- {(cfunction_ptr)f_gmtime, "gmtime", 1},- {(cfunction_ptr)f_now, "now", 1},- {(cfunction_ptr)f_current_filename, "input_filename", 1},- {(cfunction_ptr)f_current_line, "input_line_number", 1},-};-#undef LIBM_DDD_NO-#undef LIBM_DD_NO-#undef LIBM_DDD-#undef LIBM_DD--struct bytecoded_builtin { const char* name; block code; };-static block bind_bytecoded_builtins(block b) {- block builtins = gen_noop();- {- struct bytecoded_builtin builtin_defs[] = {- {"empty", gen_op_simple(BACKTRACK)},- {"not", gen_condbranch(gen_const(jv_false()),- gen_const(jv_true()))}- };- for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {- builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),- builtin_defs[i].code));- }- }- {- struct bytecoded_builtin builtin_def_1arg[] = {- {"path", BLOCK(gen_op_simple(PATH_BEGIN),- gen_call("arg", gen_noop()),- gen_op_simple(PATH_END))},- };- for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {- builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,- gen_param("arg"),- builtin_def_1arg[i].code));- }- }- {- // Note that we can now define `range` as a jq-coded function- block rangevar = gen_op_var_fresh(STOREV, "rangevar");- block rangestart = gen_op_var_fresh(STOREV, "rangestart");- block range = BLOCK(gen_op_simple(DUP),- gen_call("start", gen_noop()),- rangestart,- gen_call("end", gen_noop()),- gen_op_simple(DUP),- gen_op_bound(LOADV, rangestart),- // Reset rangevar for every value generated by "end"- rangevar,- gen_op_bound(RANGE, rangevar));- builtins = BLOCK(builtins, gen_function("range",- BLOCK(gen_param("start"), gen_param("end")),- range));- }-- return block_bind_referenced(builtins, b, OP_IS_CALL_PSEUDO);-}--#define LIBM_DD(name) "def " #name ": _" #name ";",-#define LIBM_DDD(name) "def " #name "(a;b): _" #name "(a;b);",-#define LIBM_DD_NO(name) "def " #name ": \"Error: " #name "() not found at build time\"|error;",-#define LIBM_DDD_NO(name) "def " #name "(a;b): \"Error: " #name "() not found at build time\"|error;",--static const char* const jq_builtins[] = {- "def error: error(.);",- "def map(f): [.[] | f];",- "def select(f): if f then . else empty end;",- "def sort_by(f): _sort_by_impl(map([f]));",- "def group_by(f): _group_by_impl(map([f]));",- "def unique: group_by(.) | map(.[0]);",- "def unique_by(f): group_by(f) | map(.[0]);",- "def max_by(f): _max_by_impl(map([f]));",- "def min_by(f): _min_by_impl(map([f]));",-#include "libm.h"- "def add: reduce .[] as $x (null; . + $x);",- "def del(f): delpaths([path(f)]);",- "def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",- "def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",- "def map_values(f): .[] |= f;",-- // recurse- "def recurse(f): def r: ., (f | select(. != null) | r); r;",- "def recurse(f; cond): def r: ., (f | select(cond) | r); r;",- "def recurse: recurse(.[]?);",- "def recurse_down: recurse;",-- "def to_entries: [keys_unsorted[] as $k | {key: $k, value: .[$k]}];",- "def from_entries: map({(.key // .Key // .Name): (if has(\"value\") then .value else .Value end)}) | add | .//={};",- "def with_entries(f): to_entries | map(f) | from_entries;",- "def reverse: [.[length - 1 - range(0;length)]];",- "def indices($i): if type == \"array\" and ($i|type) == \"array\" then .[$i]"- " elif type == \"array\" then .[[$i]]"- " elif type == \"string\" and ($i|type) == \"string\" then _strindices($i)"- " else .[$i] end;",- "def index($i): indices($i) | .[0];", // TODO: optimize- "def rindex($i): indices($i) | .[-1:][0];", // TODO: optimize- "def paths: path(recurse(if (type|. == \"array\" or . == \"object\") then .[] else empty end))|select(length > 0);",- "def paths(node_filter): . as $dot|paths|select(. as $p|$dot|getpath($p)|node_filter);",- "def any(generator; condition):"- " [label $out | foreach generator as $i"- " (false;"- " if . then break $out elif $i | condition then true else . end;"- " if . then . else empty end)] | length == 1;",- "def any(condition): any(.[]; condition);",- "def any: any(.);",- "def all(generator; condition): "- " [label $out | foreach generator as $i"- " (true;"- " if .|not then break $out elif $i | condition then . else false end;"- " if .|not then . else empty end)] | length == 0;",- "def all(condition): all(.[]; condition);",- "def all: all(.);",- "def isfinite: type == \"number\" and (isinfinite | not);",- "def arrays: select(type == \"array\");",- "def objects: select(type == \"object\");",- "def iterables: arrays, objects;",- "def booleans: select(type == \"boolean\");",- "def numbers: select(type == \"number\");",- "def normals: select(isnormal);",- "def finites: select(isfinite);",- "def strings: select(type == \"string\");",- "def nulls: select(type == \"null\");",- "def values: select(. != null);",- "def scalars: select(. == null or . == true or . == false or type == \"number\" or type == \"string\");",- "def scalars_or_empty: select(. == null or . == true or . == false or type == \"number\" or type == \"string\" or ((type==\"array\" or type==\"object\") and length==0));",- "def leaf_paths: paths(scalars);",- "def join($x): reduce .[] as $i (null; (.//\"\") + (if . == null then $i else $x + $i end))//\"\";",- "def _flatten($x): reduce .[] as $i ([]; if $i | type == \"array\" and $x != 0 then . + ($i | _flatten($x-1)) else . + [$i] end);",- "def flatten($x): if $x < 0 then error(\"flatten depth must not be negative\") else _flatten($x) end;",- "def flatten: _flatten(-1);",- "def range($x): range(0;$x);",- "def fromdateiso8601: strptime(\"%Y-%m-%dT%H:%M:%SZ\")|mktime;",- "def todateiso8601: strftime(\"%Y-%m-%dT%H:%M:%SZ\");",- "def fromdate: fromdateiso8601;",- "def todate: todateiso8601;",- "def match(re; mode): _match_impl(re; mode; false)|.[];",- "def match($val): ($val|type) as $vt | if $vt == \"string\" then match($val; null)"- " elif $vt == \"array\" and ($val | length) > 1 then match($val[0]; $val[1])"- " elif $vt == \"array\" and ($val | length) > 0 then match($val[0]; null)"- " else error( $vt + \" not a string or array\") end;",- "def test(re; mode): _match_impl(re; mode; true);",- "def test($val): ($val|type) as $vt | if $vt == \"string\" then test($val; null)"- " elif $vt == \"array\" and ($val | length) > 1 then test($val[0]; $val[1])"- " elif $vt == \"array\" and ($val | length) > 0 then test($val[0]; null)"- " else error( $vt + \" not a string or array\") end;",- "def capture(re; mods): match(re; mods) | reduce ( .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair ({}; . + $pair);",- "def capture($val): ($val|type) as $vt | if $vt == \"string\" then capture($val; null)"- " elif $vt == \"array\" and ($val | length) > 1 then capture($val[0]; $val[1])"- " elif $vt == \"array\" and ($val | length) > 0 then capture($val[0]; null)"- " else error( $vt + \" not a string or array\") end;",- "def scan(re):"- " match(re; \"g\")"- " | if (.captures|length > 0)"- " then [ .captures | .[] | .string ]"- " else .string"- " end ;",- //- // If input is an array, then emit a stream of successive subarrays of length n (or less),- // and similarly for strings.- "def _nwise(a; $n): if a|length <= $n then a else a[0:$n] , _nwise(a[$n:]; $n) end;",- "def _nwise($n): _nwise(.; $n);",- //- // splits/1 produces a stream; split/1 is retained for backward compatibility.- "def splits($re; flags): . as $s"- // # multiple occurrences of "g" are acceptable- " | [ match($re; \"g\" + flags) | (.offset, .offset + .length) ]"- " | [0] + . +[$s|length]"- " | _nwise(2)"- " | $s[.[0]:.[1] ] ;",- "def splits($re): splits($re; null);",- //- // split emits an array for backward compatibility- "def split($re; flags): [ splits($re; flags) ];",- //- // If s contains capture variables, then create a capture object and pipe it to s- "def sub($re; s):"- " . as $in"- " | [match($re)]"- " | if length == 0 then $in"- " else .[0]"- " | . as $r"- // # create the \"capture\" object:- " | reduce ( $r | .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair"- " ({}; . + $pair)"- " | $in[0:$r.offset] + s + $in[$r.offset+$r.length:]"- " end ;",- //- // If s contains capture variables, then create a capture object and pipe it to s- "def sub($re; s; flags):"- " def subg: explode | select(. != 103) | implode;"- // # "fla" should be flags with all occurrences of g removed; gs should be non-nil if flags has a g- " def sub1(fla; gs):"- " def mysub:"- " . as $in"- " | [match($re; fla)]"- " | if length == 0 then $in"- " else .[0] as $edit"- " | ($edit | .offset + .length) as $len"- // # create the "capture" object:- " | reduce ( $edit | .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair"- " ({}; . + $pair)"- " | $in[0:$edit.offset]"- " + s"- " + ($in[$len:] | if gs then mysub else . end)"- " end ;"- " mysub ;"- " (flags | index(\"g\")) as $gs"- " | (flags | if $gs then subg else . end) as $fla"- " | sub1($fla; $gs);",- //- "def sub($re; s): sub($re; s; \"\");",- // repeated substitution of re (which may contain named captures)- "def gsub($re; s; flags): sub($re; s; flags + \"g\");",- "def gsub($re; s): sub($re; s; \"g\");",-- //#######################################################################- // range/3, with a `by` expression argument- "def range($init; $upto; $by): "- " def _range: "- " if ($by > 0 and . < $upto) or ($by < 0 and . > $upto) then ., ((.+$by)|_range) else . end; "- " if $by == 0 then $init else $init|_range end | select(($by > 0 and . < $upto) or ($by < 0 and . > $upto));",- // generic iterator/generator- "def while(cond; update): "- " def _while: "- " if cond then ., (update | _while) else empty end; "- " _while;",- "def until(cond; next): "- " def _until: "- " if cond then . else (next|_until) end;"- " _until;",- "def limit($n; exp): if $n < 0 then exp else label $out | foreach exp as $item ([$n, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1]) end;",- "def first(g): label $out | foreach g as $item ([false, null]; if .[0]==true then break $out else [true, $item] end; .[1]);",- "def last(g): reduce g as $item (null; $item);",- "def nth($n; g): if $n < 0 then error(\"nth doesn't support negative indices\") else last(limit($n + 1; g)) end;",- "def first: .[0];",- "def last: .[-1];",- "def nth($n): .[$n];",- "def combinations:"- " if length == 0 then [] else"- " .[0][] as $x"- " | (.[1:] | combinations) as $y"- " | [$x] + $y"- " end;",- "def combinations(n):"- " . as $dot"- " | [range(n) | $dot]"- " | combinations;",- // # transpose a possibly jagged matrix, quickly;- // # rows are padded with nulls so the result is always rectangular.- "def transpose:"- " if . == [] then []"- " else . as $in"- " | (map(length) | max) as $max"- " | length as $length"- " | reduce range(0; $max) as $j"- " ([]; . + [reduce range(0;$length) as $i ([]; . + [ $in[$i][$j] ] )] )"- " end;",- "def in(xs): . as $x | xs | has($x);",- "def inside(xs): . as $x | xs | contains($x);",- "def input: _input;",- "def repeat(exp): "- " def _repeat: "- " exp, _repeat;"- " _repeat;",- "def inputs: try repeat(_input) catch if .==\"break\" then empty else .|error end;",- // # like ruby's downcase - only characters A to Z are affected- "def ascii_downcase:"- " explode | map( if 65 <= . and . <= 90 then . + 32 else . end) | implode;",- // # like ruby's upcase - only characters a to z are affected- "def ascii_upcase:"- " explode | map( if 97 <= . and . <= 122 then . - 32 else . end) | implode;",-- // Streaming utilities- "def truncate_stream(stream):"- " . as $n | null | stream | . as $input | if (.[0]|length) > $n then setpath([0];$input[0][1:]) else empty end;",- "def fromstream(i):"- " foreach i as $item ("- " [null,false,null,false];"- " if ($item[0]|length) == 0 then [null,false,.[2],.[3]]"- " elif ($item|length) == 1 and ($item[0]|length) < 2 then [null,false,.[0],.[1]]"- " else . end |"- " . as $state |"- " if ($item|length) > 1 and ($item[0]|length) > 0 then"- " [.[0]|setpath(($item|.[0]); ($item|.[1])), "- " true, "- " $state[2], "- " $state[3]] "- " else ."- " end;"- " if ($item[0]|length) == 1 and ($item|length == 1) and .[3] then .[2] else empty end,"- " if ($item[0]|length) == 0 then $item[1] else empty end"- " );",- "def tostream:\n"- " {string:true,number:true,boolean:true,null:true} as $leaf_types |\n"- " . as $dot |\n"- " if $leaf_types[$dot|type] or length==0 then [[],$dot]\n"- " else\n"- " # We really need a _streaming_ form of `keys`.\n"- " # We can use `range` for arrays, but not for objects.\n"- " keys as $keys |\n"- " $keys[-1] as $last|\n"- " ((# for each key\n"- " $keys[] | . as $key |\n"- " $dot[$key] | . as $dot |\n"- " # recurse on each key/value\n"- " tostream|.[0]|=[$key]+.),\n"- " # then add the closing marker\n"- " [[$last]])\n"- " end;",--- // # Assuming the input array is sorted, bsearch/1 returns- // # the index of the target if the target is in the input array; and otherwise- // # (-1 - ix), where ix is the insertion point that would leave the array sorted.- // # If the input is not sorted, bsearch will terminate but with irrelevant results.- "def bsearch(target):"- " if length == 0 then -1"- " elif length == 1 then"- " if target == .[0] then 0 elif target < .[0] then -1 else -2 end"- " else . as $in"- "" // # state variable: [start, end, answer]- "" // # where start and end are the upper and lower offsets to use.- " | [0, length-1, null]"- " | until( .[0] > .[1] ;"- " if .[2] != null then (.[1] = -1)" // # i.e. break- " else"- " ( ( (.[1] + .[0]) / 2 ) | floor ) as $mid"- " | $in[$mid] as $monkey"- " | if $monkey == target then (.[2] = $mid)" // # success- " elif .[0] == .[1] then (.[1] = -1)" // # failure- " elif $monkey < target then (.[0] = ($mid + 1))"- " else (.[1] = ($mid - 1))"- " end"- " end )"- " | if .[2] == null then" // # compute the insertion point- " if $in[ .[0] ] < target then (-2 -.[0])"- " else (-1 -.[0])"- " end"- " else .[2]"- " end"- " end;",-- // # Apply f to composite entities recursively, and to atoms- "def walk(f):"- " . as $in"- " | if type == \"object\" then"- " reduce keys[] as $key"- " ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f"- " elif type == \"array\" then map( walk(f) ) | f"- " else f"- " end;",-};--#undef LIBM_DDD_NO-#undef LIBM_DD_NO-#undef LIBM_DDD-#undef LIBM_DD---static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {- struct locfile* src;- src = locfile_init(jq, "<builtin>", code, strlen(code));- block funcs;- int nerrors = jq_parse_library(src, &funcs);- if (nerrors == 0) {- *bb = block_bind_referenced(funcs, *bb, OP_IS_CALL_PSEUDO);- }- locfile_free(src);- return nerrors;-}--static int slurp_lib(jq_state *jq, block* bb) {- int nerrors = 0;- char* home = getenv("HOME");- if (home) { // silently ignore no $HOME- jv filename = jv_string_append_str(jv_string(home), "/.jq");- jv data = jv_load_file(jv_string_value(filename), 1);- if (jv_is_valid(data)) {- nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );- }- jv_free(filename);- jv_free(data);- }- return nerrors;-}--int builtins_bind(jq_state *jq, block* bb) {- int nerrors = slurp_lib(jq, bb);- if (nerrors) {- block_free(*bb);- return nerrors;- }- for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {- nerrors = builtins_bind_one(jq, bb, jq_builtins[i]);- assert(!nerrors);- }- *bb = bind_bytecoded_builtins(*bb);- *bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);- return nerrors;-}
builtin.h
@@ -1,10 +0,0 @@-#ifndef BUILTIN_H-#define BUILTIN_H--#include "jq.h"-#include "bytecode.h"-#include "compile.h"--int builtins_bind(jq_state *, block*);--#endif
bytecode.c
@@ -1,161 +0,0 @@-#include <stdio.h>-#include <stdint.h>-#include <stdlib.h>--#include "bytecode.h"-#include "jv_alloc.h"--// flags, length-#define NONE 0, 1-#define CONSTANT OP_HAS_CONSTANT, 2-#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3-#define GLOBAL (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4-#define BRANCH OP_HAS_BRANCH, 2-#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3-#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4-#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0-#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2--#define OP(name, imm, in, out) \- {name, #name, imm, in, out},--static const struct opcode_description opcode_descriptions[] = {-#include "opcode_list.h"-};--static const struct opcode_description invalid_opcode_description = {- -1, "#INVALID", 0, 0, 0, 0-};---const struct opcode_description* opcode_describe(opcode op) {- if ((int)op >= 0 && (int)op < NUM_OPCODES) {- return &opcode_descriptions[op];- } else {- return &invalid_opcode_description;- }-}---int bytecode_operation_length(uint16_t* codeptr) {- int length = opcode_describe(*codeptr)->length;- if (*codeptr == CALL_JQ || *codeptr == TAIL_CALL_JQ) {- length += codeptr[1] * 2;- }- return length;-}--static void dump_code(int indent, struct bytecode* bc) {- int pc = 0;- while (pc < bc->codelen) {- printf("%*s", indent, "");- dump_operation(bc, bc->code + pc);- printf("\n");- pc += bytecode_operation_length(bc->code + pc);- }-}--static void symbol_table_free(struct symbol_table* syms) {- jv_mem_free(syms->cfunctions);- jv_free(syms->cfunc_names);- jv_mem_free(syms);-}--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 struct bytecode* getlevel(struct bytecode* bc, int level) {- while (level > 0) {- bc = bc->parent;- level--;- }- return bc;-}--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);- }- }-}--void bytecode_free(struct bytecode* bc) {- if (!bc)- return;- jv_mem_free(bc->code);- jv_free(bc->constants);- for (int i=0; i<bc->nsubfunctions; i++)- bytecode_free(bc->subfunctions[i]);- if (!bc->parent)- symbol_table_free(bc->globals);- jv_mem_free(bc->subfunctions);- jv_free(bc->debuginfo);- jv_mem_free(bc);-}
bytecode.h
@@ -1,92 +0,0 @@-#ifndef BYTECODE_H-#define BYTECODE_H-#include <stdint.h>--#include "jv.h"--typedef enum {-#define OP(name, imm, in, out) name,-#include "opcode_list.h"-#undef OP-} opcode;--enum {- NUM_OPCODES =-#define OP(name, imm, in, out) +1-#include "opcode_list.h"-#undef OP-};--enum {- OP_HAS_CONSTANT = 2,- OP_HAS_VARIABLE = 4,- OP_HAS_BRANCH = 8,- OP_HAS_CFUNC = 32,- OP_HAS_UFUNC = 64,- OP_IS_CALL_PSEUDO = 128,- OP_HAS_BINDING = 1024,- // NOTE: Not actually part of any op -- a pseudo-op flag for special- // handling of `break`.- OP_BIND_WILDCARD = 2048,-};-struct opcode_description {- opcode op;- const char* name;-- int flags;-- // length in 16-bit units- int length;-- int stack_in, stack_out;-};--const struct opcode_description* opcode_describe(opcode op);---#define MAX_CFUNCTION_ARGS 10-typedef void (*cfunction_ptr)();-struct cfunction {- cfunction_ptr fptr;- const char* name;- int nargs;-};--struct symbol_table {- struct cfunction* cfunctions;- int ncfunctions;- jv cfunc_names;-};--// The bytecode format matters in:-// execute.c - interpreter-// compile.c - compiler-// bytecode.c - disassembler--#define ARG_NEWCLOSURE 0x1000--struct bytecode {- uint16_t* code;- int codelen;-- int nlocals;- int nclosures;-- jv constants; // JSON array of constants- struct symbol_table* globals;-- struct bytecode** subfunctions;- int nsubfunctions;-- struct bytecode* parent;-- jv debuginfo;-};--void dump_disassembly(int, struct bytecode* code);-void dump_operation(struct bytecode* bc, uint16_t* op);--int bytecode_operation_length(uint16_t* codeptr);-void bytecode_free(struct bytecode* bc);--#endif
compile.c
@@ -1,1213 +0,0 @@-#ifndef _GNU_SOURCE-#define _GNU_SOURCE // for strdup-#endif-#include <assert.h>-#include <math.h>-#include <string.h>-#include <stdlib.h>-#include "compile.h"-#include "bytecode.h"-#include "locfile.h"-#include "jv_alloc.h"-#include "linker.h"--/*- The intermediate representation for jq filters is as a sequence of- struct inst, which form a doubly-linked list via the next and prev- pointers.-- A "block" represents a sequence of "struct inst", which may be- empty.-- Blocks are generated by the parser bottom-up, so may have free- variables (refer to things not defined). See inst.bound_by and- inst.symbol.- */-struct inst {- struct inst* next;- struct inst* prev;-- opcode op;-- struct {- uint16_t intval;- struct inst* target;- jv constant;- const struct cfunction* cfunc;- } imm;-- struct locfile* locfile;- location source;-- // Binding- // An instruction requiring binding (for parameters/variables/functions)- // is in one of three states:- // inst->bound_by = NULL - Unbound free variable- // inst->bound_by = inst - This instruction binds a variable- // inst->bound_by = other - Uses variable bound by other instruction- // Unbound instructions (references to other things that may or may not- // exist) are created by "gen_foo_unbound", and bindings are created by- // block_bind(definition, body), which binds all instructions in- // body which are unboudn and refer to "definition" by name.- struct inst* bound_by;- char* symbol;-- int nformals;- int nactuals;-- block subfn; // used by CLOSURE_CREATE (body of function)- block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments)-- // This instruction is compiled as part of which function?- // (only used during block_compile)- struct bytecode* compiled;-- int bytecode_pos; // position just after this insn-};--static inst* inst_new(opcode op) {- inst* i = jv_mem_alloc(sizeof(inst));- i->next = i->prev = 0;- i->op = op;- i->bytecode_pos = -1;- i->bound_by = 0;- i->symbol = 0;- i->nformals = -1;- i->nactuals = -1;- i->subfn = gen_noop();- i->arglist = gen_noop();- i->source = UNKNOWN_LOCATION;- i->locfile = 0;- return i;-}--static void inst_free(struct inst* i) {- jv_mem_free(i->symbol);- block_free(i->subfn);- block_free(i->arglist);- if (i->locfile)- locfile_free(i->locfile);- if (opcode_describe(i->op)->flags & OP_HAS_CONSTANT) {- jv_free(i->imm.constant);- }- jv_mem_free(i);-}--static block inst_block(inst* i) {- block b = {i,i};- return b;-}--int block_is_single(block b) {- return b.first && b.first == b.last;-}--static inst* block_take(block* b) {- if (b->first == 0) return 0;- inst* i = b->first;- if (i->next) {- i->next->prev = 0;- b->first = i->next;- i->next = 0;- } else {- b->first = 0;- b->last = 0;- }- return i;-}--block gen_location(location loc, struct locfile* l, block b) {- for (inst* i = b.first; i; i = i->next) {- if (i->source.start == UNKNOWN_LOCATION.start &&- i->source.end == UNKNOWN_LOCATION.end) {- i->source = loc;- i->locfile = locfile_retain(l);- }- }- return b;-}--block gen_noop() {- block b = {0,0};- return b;-}--int block_is_noop(block b) {- return (b.first == 0 && b.last == 0);-}--block gen_op_simple(opcode op) {- assert(opcode_describe(op)->length == 1);- return inst_block(inst_new(op));-}---block gen_const(jv constant) {- assert(opcode_describe(LOADK)->flags & OP_HAS_CONSTANT);- inst* i = inst_new(LOADK);- i->imm.constant = constant;- return inst_block(i);-}--block gen_const_global(jv constant, const char *name) {- assert((opcode_describe(STORE_GLOBAL)->flags & (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING)) ==- (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING));- inst* i = inst_new(STORE_GLOBAL);- i->imm.constant = constant;- i->symbol = strdup(name);- return inst_block(i);-}--int block_is_const(block b) {- return (block_is_single(b) && b.first->op == LOADK);-}--int block_is_const_inf(block b) {- return (block_is_single(b) && b.first->op == LOADK &&- jv_get_kind(b.first->imm.constant) == JV_KIND_NUMBER &&- isinf(jv_number_value(b.first->imm.constant)));-}--jv_kind block_const_kind(block b) {- assert(block_is_const(b));- return jv_get_kind(b.first->imm.constant);-}--jv block_const(block b) {- assert(block_is_const(b));- return jv_copy(b.first->imm.constant);-}--block gen_op_target(opcode op, block target) {- assert(opcode_describe(op)->flags & OP_HAS_BRANCH);- assert(target.last);- inst* i = inst_new(op);- i->imm.target = target.last;- return inst_block(i);-}--block gen_op_targetlater(opcode op) {- assert(opcode_describe(op)->flags & OP_HAS_BRANCH);- inst* i = inst_new(op);- i->imm.target = 0;- return inst_block(i);-}-void inst_set_target(block b, block target) {- assert(block_is_single(b));- assert(opcode_describe(b.first->op)->flags & OP_HAS_BRANCH);- assert(target.last);- b.first->imm.target = target.last;-}--block gen_op_unbound(opcode op, const char* name) {- assert(opcode_describe(op)->flags & OP_HAS_BINDING);- inst* i = inst_new(op);- i->symbol = strdup(name);- return inst_block(i);-}--block gen_op_var_fresh(opcode op, const char* name) {- assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);- return block_bind(gen_op_unbound(op, name),- gen_noop(), OP_HAS_VARIABLE);-}--block gen_op_bound(opcode op, block binder) {- assert(block_is_single(binder));- block b = gen_op_unbound(op, binder.first->symbol);- b.first->bound_by = binder.first;- return b;-}---static void inst_join(inst* a, inst* b) {- assert(a && b);- assert(!a->next);- assert(!b->prev);- a->next = b;- b->prev = a;-}--void block_append(block* b, block b2) {- if (b2.first) {- if (b->last) {- inst_join(b->last, b2.first);- } else {- b->first = b2.first;- }- b->last = b2.last;- }-}--block block_join(block a, block b) {- block c = a;- block_append(&c, b);- return c;-}--int block_has_only_binders_and_imports(block binders, int bindflags) {- bindflags |= OP_HAS_BINDING;- for (inst* curr = binders.first; curr; curr = curr->next) {- if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != DEPS && curr->op != MODULEMETA) {- return 0;- }- }- return 1;-}--static int inst_is_binder(inst *i, int bindflags) {- return !((opcode_describe(i->op)->flags & bindflags) != bindflags && i->op != MODULEMETA);-}--int block_has_only_binders(block binders, int bindflags) {- bindflags |= OP_HAS_BINDING;- bindflags &= ~OP_BIND_WILDCARD;- for (inst* curr = binders.first; curr; curr = curr->next) {- if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != MODULEMETA) {- return 0;- }- }- return 1;-}--// Count a binder's (function) formal params-static int block_count_formals(block b) {- int args = 0;- if (b.first->op == CLOSURE_CREATE_C)- return b.first->imm.cfunc->nargs - 1;- for (inst* i = b.first->arglist.first; i; i = i->next) {- assert(i->op == CLOSURE_PARAM);- args++;- }- return args;-}--// Count a call site's actual params-static int block_count_actuals(block b) {- int args = 0;- for (inst* i = b.first; i; i = i->next) {- switch (i->op) {- default: assert(0 && "Unknown function type"); break;- case CLOSURE_CREATE:- case CLOSURE_PARAM:- case CLOSURE_CREATE_C:- args++;- break;- }- }- return args;-}--static int block_count_refs(block binder, block body) {- int nrefs = 0;- for (inst* i = body.first; i; i = i->next) {- if (i != binder.first && i->bound_by == binder.first) {- nrefs++;- }- // counting recurses into closures- nrefs += block_count_refs(binder, i->subfn);- // counting recurses into argument list- nrefs += block_count_refs(binder, i->arglist);- }- return nrefs;-}--static int block_bind_subblock(block binder, block body, int bindflags, int break_distance) {- assert(block_is_single(binder));- assert((opcode_describe(binder.first->op)->flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD));- assert(binder.first->symbol);- assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);- assert(break_distance >= 0);-- binder.first->bound_by = binder.first;- if (binder.first->nformals == -1)- binder.first->nformals = block_count_formals(binder);- int nrefs = 0;- for (inst* i = body.first; i; i = i->next) {- int flags = opcode_describe(i->op)->flags;- if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by == 0 &&- (!strcmp(i->symbol, binder.first->symbol) ||- // Check for break/break2/break3; see parser.y- ((bindflags & OP_BIND_WILDCARD) && i->symbol[0] == '*' &&- break_distance <= 3 && (i->symbol[1] == '1' + break_distance) &&- i->symbol[2] == '\0'))) {- // bind this instruction- if (i->op == CALL_JQ && i->nactuals == -1)- i->nactuals = block_count_actuals(i->arglist);- if (i->nactuals == -1 || i->nactuals == binder.first->nformals) {- i->bound_by = binder.first;- nrefs++;- }- } else if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by != 0 &&- !strncmp(binder.first->symbol, "*anonlabel", sizeof("*anonlabel") - 1) &&- !strncmp(i->symbol, "*anonlabel", sizeof("*anonlabel") - 1)) {- // Increment the break distance required for this binder to match- // a break whenever we come across a STOREV of *anonlabel...- break_distance++;- }- // binding recurses into closures- nrefs += block_bind_subblock(binder, i->subfn, bindflags, break_distance);- // binding recurses into argument list- nrefs += block_bind_subblock(binder, i->arglist, bindflags, break_distance);- }- return nrefs;-}--static int block_bind_each(block binder, block body, int bindflags) {- assert(block_has_only_binders(binder, bindflags));- bindflags |= OP_HAS_BINDING;- int nrefs = 0;- for (inst* curr = binder.first; curr; curr = curr->next) {- nrefs += block_bind_subblock(inst_block(curr), body, bindflags, 0);- }- return nrefs;-}--block block_bind(block binder, block body, int bindflags) {- block_bind_each(binder, body, bindflags);- return block_join(binder, body);-}--block block_bind_library(block binder, block body, int bindflags, const char *libname) {- bindflags |= OP_HAS_BINDING;- int nrefs = 0;- int matchlen = (libname == NULL) ? 0 : strlen(libname);- char *matchname = jv_mem_alloc(matchlen+2+1);- matchname[0] = '\0';- if (libname != NULL && libname[0] != '\0') {- strcpy(matchname,libname);- strcpy(matchname+matchlen, "::");- matchlen += 2;- }- assert(block_has_only_binders(binder, bindflags));- for (inst *curr = binder.first; curr; curr = curr->next) {- int bindflags2 = bindflags;- char* cname = curr->symbol;- char* tname = jv_mem_alloc(strlen(curr->symbol)+matchlen+1);- strcpy(tname, matchname);- strcpy(tname+matchlen, curr->symbol);-- // Ew- if ((opcode_describe(curr->op)->flags & (OP_HAS_VARIABLE | OP_HAS_CONSTANT)))- bindflags2 = OP_HAS_VARIABLE | OP_HAS_BINDING;-- // This mutation is ugly, even if we undo it- curr->symbol = tname;- nrefs += block_bind_subblock(inst_block(curr), body, bindflags2, 0);- curr->symbol = cname;- free(tname);- }- free(matchname);- return body; // We don't return a join because we don't want those sticking around...-}--// Bind binder to body and throw away any defs in binder not referenced-// (directly or indirectly) from body.-block block_bind_referenced(block binder, block body, int bindflags) {- assert(block_has_only_binders(binder, bindflags));- bindflags |= OP_HAS_BINDING;- block refd = gen_noop();- block unrefd = gen_noop();- int nrefs;- for (int last_kept = 0, kept = 0; ; ) {- for (inst* curr; (curr = block_take(&binder));) {- block b = inst_block(curr);- nrefs = block_bind_each(b, body, bindflags);- // Check if this binder is referenced from any of the ones we- // already know are referenced by body.- nrefs += block_count_refs(b, refd);- nrefs += block_count_refs(b, body);- if (nrefs) {- refd = BLOCK(refd, b);- kept++;- } else {- unrefd = BLOCK(unrefd, b);- }- }- if (kept == last_kept)- break;- last_kept = kept;- binder = unrefd;- unrefd = gen_noop();- }- block_free(unrefd);- return block_join(refd, body);-}--block block_drop_unreferenced(block body) {- inst* curr;- block refd = gen_noop();- block unrefd = gen_noop();- int drop;- do {- drop = 0;- while ((curr = block_take(&body)) && curr->op != TOP) {- block b = inst_block(curr);- if (block_count_refs(b,refd) + block_count_refs(b,body) == 0) {- unrefd = BLOCK(unrefd, b);- drop++;- } else {- refd = BLOCK(refd, b);- }- }- if (curr && curr->op == TOP) {- body = BLOCK(inst_block(curr),body);- }- body = BLOCK(refd, body);- refd = gen_noop();- } while (drop != 0);- block_free(unrefd);- return body;-}--jv block_take_imports(block* body) {- jv imports = jv_array();-- inst* top = NULL;- if (body->first && body->first->op == TOP) {- top = block_take(body);- }- while (body->first && (body->first->op == MODULEMETA || body->first->op == DEPS)) {- inst* dep = block_take(body);- if (dep->op == DEPS) {- imports = jv_array_append(imports, jv_copy(dep->imm.constant));- }- inst_free(dep);- }- if (top) {- *body = block_join(inst_block(top),*body);- }- return imports;-}--block gen_module(block metadata) {- inst* i = inst_new(MODULEMETA);- i->imm.constant = block_const(metadata);- if (jv_get_kind(i->imm.constant) != JV_KIND_OBJECT)- i->imm.constant = jv_object_set(jv_object(), jv_string("metadata"), i->imm.constant);- block_free(metadata);- return inst_block(i);-}--jv block_module_meta(block b) {- if (b.first != NULL && b.first->op == MODULEMETA)- return jv_copy(b.first->imm.constant);- return jv_null();-}--block gen_import(const char* name, const char* as, int is_data) {- inst* i = inst_new(DEPS);- jv meta = jv_object();- if (as != NULL)- meta = jv_object_set(meta, jv_string("as"), jv_string(as));- meta = jv_object_set(meta, jv_string("is_data"), is_data ? jv_true() : jv_false());- meta = jv_object_set(meta, jv_string("relpath"), jv_string(name));- i->imm.constant = meta;- return inst_block(i);-}--block gen_import_meta(block import, block metadata) {- assert(block_is_single(import) && import.first->op == DEPS);- assert(block_is_const(metadata) && block_const_kind(metadata) == JV_KIND_OBJECT);- inst *i = import.first;- i->imm.constant = jv_object_merge(block_const(metadata), i->imm.constant);- block_free(metadata);- return import;-}--block gen_function(const char* name, block formals, block body) {- inst* i = inst_new(CLOSURE_CREATE);- for (inst* i = formals.last; i; i = i->prev) {- if (i->op == CLOSURE_PARAM_REGULAR) {- i->op = CLOSURE_PARAM;- body = gen_var_binding(gen_call(i->symbol, gen_noop()), i->symbol, body);- }- block_bind_subblock(inst_block(i), body, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);- }- i->subfn = body;- i->symbol = strdup(name);- i->arglist = formals;- block b = inst_block(i);- block_bind_subblock(b, b, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);- return b;-}--block gen_param_regular(const char* name) {- return gen_op_unbound(CLOSURE_PARAM_REGULAR, name);-}--block gen_param(const char* name) {- return gen_op_unbound(CLOSURE_PARAM, name);-}--block gen_lambda(block body) {- return gen_function("@lambda", gen_noop(), body);-}--block gen_call(const char* name, block args) {- block b = gen_op_unbound(CALL_JQ, name);- b.first->arglist = args;- return b;-}----block gen_subexp(block a) {- return BLOCK(gen_op_simple(SUBEXP_BEGIN), a, gen_op_simple(SUBEXP_END));-}--block gen_both(block a, block b) {- block jump = gen_op_targetlater(JUMP);- block fork = gen_op_target(FORK, jump);- block c = BLOCK(fork, a, jump, b);- inst_set_target(jump, c);- return c;-}--block gen_const_object(block expr) {- int is_const = 1;- jv o = jv_object();- jv k = jv_null();- jv v = jv_null();- for (inst *i = expr.first; i; i = i->next) {- if (i->op != SUBEXP_BEGIN ||- i->next == NULL ||- i->next->op != LOADK ||- i->next->next == NULL ||- i->next->next->op != SUBEXP_END) {- is_const = 0;- break;- }- k = jv_copy(i->next->imm.constant);- i = i->next->next->next;- if (i == NULL ||- i->op != SUBEXP_BEGIN ||- i->next == NULL ||- i->next->op != LOADK ||- i->next->next == NULL ||- i->next->next->op != SUBEXP_END) {- is_const = 0;- break;- }- v = jv_copy(i->next->imm.constant);- i = i->next->next->next;- if (i == NULL || i->op != INSERT) {- is_const = 0;- break;- }- o = jv_object_set(o, k, v);- k = jv_null();- v = jv_null();- }- if (!is_const) {- jv_free(o);- jv_free(k);- jv_free(v);- block b = {0,0};- return b;- }- block_free(expr);- return gen_const(o);-}--static block gen_const_array(block expr) {- /*- * An expr of all constant elements looks like this:- *- * 0009 FORK 0027- * 0011 FORK 0023- * 0013 FORK 0019- * 0015 LOADK 1- * 0017 JUMP 0021- * 0019 LOADK 2- * 0021 JUMP 0025- * 0023 LOADK 3- * 0025 JUMP 0029- * 0027 LOADK 4- *- * That's: N-1 commas for N elements, N LOADKs, and a JUMP between- * every LOADK. The sequence ends in a LOADK. Any deviation and it's- * not a list of constants.- *- * Here we check for this pattern almost exactly. We don't check that- * the targets of the FORK and JUMP instructions are in the right- * sequence.- */- int all_const = 1;- int commas = 0;- int normal = 1;- jv a = jv_array();- for (inst *i = expr.first; i; i = i->next) {- if (i->op == FORK) {- commas++;- if (i->imm.target == NULL || i->imm.target->op != JUMP ||- jv_array_length(jv_copy(a)) > 0) {- normal = 0;- break;- }- } else if (all_const && i->op == LOADK) {- if (i->next != NULL && i->next->op != JUMP) {- normal = 0;- break;- }- a = jv_array_append(a, jv_copy(i->imm.constant));- } else if (i->op != JUMP || i->imm.target == NULL ||- i->imm.target->op != LOADK) {- all_const = 0;- }- }-- if (all_const && normal &&- (expr.last == NULL || expr.last->op == LOADK) &&- jv_array_length(jv_copy(a)) == commas + 1) {- block_free(expr);- return gen_const(a);- }-- jv_free(a);- block b = {0,0};- return b;-}--block gen_collect(block expr) {- block const_array = gen_const_array(expr);- if (const_array.first != NULL)- return const_array;-- block array_var = gen_op_var_fresh(STOREV, "collect");- block c = BLOCK(gen_op_simple(DUP), gen_const(jv_array()), array_var);-- block tail = BLOCK(gen_op_bound(APPEND, array_var),- gen_op_simple(BACKTRACK));-- return BLOCK(c,- gen_op_target(FORK, tail),- expr,- tail,- gen_op_bound(LOADVN, array_var));-}--static block bind_matcher(block matcher, block body) {- // cannot call block_bind(matcher, body) because that requires- // block_has_only_binders(matcher), which is not true here as matchers- // may also contain code to extract the correct elements- for (inst* i = matcher.first; i; i = i->next) {- if (i->op == STOREV && !i->bound_by)- block_bind_subblock(inst_block(i), body, OP_HAS_VARIABLE, 0);- }- return BLOCK(matcher, body);-}--block gen_reduce(block source, block matcher, block init, block body) {- block res_var = gen_op_var_fresh(STOREV, "reduce");- block loop = BLOCK(gen_op_simple(DUPN),- source,- bind_matcher(matcher,- BLOCK(gen_op_bound(LOADVN, res_var),- body,- gen_op_bound(STOREV, res_var))),- gen_op_simple(BACKTRACK));- return BLOCK(gen_op_simple(DUP),- init,- res_var,- gen_op_target(FORK, loop),- loop,- gen_op_bound(LOADVN, res_var));-}--block gen_foreach(block source, block matcher, block init, block update, block extract) {- block output = gen_op_targetlater(JUMP);- block state_var = gen_op_var_fresh(STOREV, "foreach");- block loop = BLOCK(gen_op_simple(DUPN),- // get a value from the source expression:- source,- // destructure the value into variable(s) for all the code- // in the body to see- bind_matcher(matcher,- // load the loop state variable- BLOCK(gen_op_bound(LOADVN, state_var),- // generate updated state- update,- // save the updated state for value extraction- gen_op_simple(DUP),- // save new state- gen_op_bound(STOREV, state_var),- // extract an output...- extract,- // ...and output it by jumping- // past the BACKTRACK that comes- // right after the loop body,- // which in turn is there- // because...- //- // (Incidentally, extract can also- // backtrack, e.g., if it calls- // empty, in which case we don't- // get here.)- output)));- block foreach = BLOCK(gen_op_simple(DUP),- init,- state_var,- gen_op_target(FORK, loop),- loop,- // ...at this point `foreach`'s original input- // will be on top of the stack, and we don't- // want to output it, so we backtrack.- gen_op_simple(BACKTRACK));- inst_set_target(output, foreach); // make that JUMP go bast the BACKTRACK at the end of the loop- return foreach;-}--block gen_definedor(block a, block b) {- // var found := false- block found_var = gen_op_var_fresh(STOREV, "found");- block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);-- // if found, backtrack. Otherwise execute b- block backtrack = gen_op_simple(BACKTRACK);- block tail = BLOCK(gen_op_simple(DUP),- gen_op_bound(LOADV, found_var),- gen_op_target(JUMP_F, backtrack),- backtrack,- gen_op_simple(POP),- b);-- // try again- block if_notfound = gen_op_simple(BACKTRACK);-- // found := true, produce result- block if_found = BLOCK(gen_op_simple(DUP),- gen_const(jv_true()),- gen_op_bound(STOREV, found_var),- gen_op_target(JUMP, tail));-- return BLOCK(init,- gen_op_target(FORK, if_notfound),- a,- gen_op_target(JUMP_F, if_found),- if_found,- if_notfound,- tail);-}--int block_has_main(block top) {- for (inst *c = top.first; c; c = c->next) {- if (c->op == TOP)- return 1;- }- return 0;-}--int block_is_funcdef(block b) {- if (b.first != NULL && b.first->op == CLOSURE_CREATE)- return 1;- return 0;-}--block gen_condbranch(block iftrue, block iffalse) {- iftrue = BLOCK(iftrue, gen_op_target(JUMP, iffalse));- return BLOCK(gen_op_target(JUMP_F, iftrue), iftrue, iffalse);-}--block gen_and(block a, block b) {- // a and b = if a then (if b then true else false) else false- return BLOCK(gen_op_simple(DUP), a,- gen_condbranch(BLOCK(gen_op_simple(POP),- b,- gen_condbranch(gen_const(jv_true()),- gen_const(jv_false()))),- BLOCK(gen_op_simple(POP), gen_const(jv_false()))));-}--block gen_or(block a, block b) {- // a or b = if a then true else (if b then true else false)- return BLOCK(gen_op_simple(DUP), a,- gen_condbranch(BLOCK(gen_op_simple(POP), gen_const(jv_true())),- BLOCK(gen_op_simple(POP),- b,- gen_condbranch(gen_const(jv_true()),- gen_const(jv_false())))));-}--block gen_var_binding(block var, const char* name, block body) {- return gen_destructure(var, gen_op_unbound(STOREV, name), body);-}--block gen_array_matcher(block left, block curr) {- int index;- if (block_is_noop(left))- index = 0;- else {- // `left` was returned by this function, so the third inst is the- // constant containing the previously used index- assert(left.first->op == DUP);- assert(left.first->next->op == SUBEXP_BEGIN);- assert(left.first->next->next->op == LOADK);- index = 1 + (int) jv_number_value(left.first->next->next->imm.constant);- }-- // `left` goes at the end so that the const index is in a predictable place- return BLOCK(gen_op_simple(DUP), gen_subexp(gen_const(jv_number(index))),- gen_op_simple(INDEX), curr, left);-}--block gen_object_matcher(block name, block curr) {- return BLOCK(gen_op_simple(DUP), gen_subexp(name), gen_op_simple(INDEX),- curr);-}--block gen_destructure(block var, block matcher, block body) {- // var bindings can be added after coding the program; leave the TOP first.- block top = gen_noop();- if (body.first && body.first->op == TOP)- top = inst_block(block_take(&body));-- return BLOCK(top, gen_op_simple(DUP), var, bind_matcher(matcher, body));-}--// Like gen_var_binding(), but bind `break`'s wildcard unbound variable-static block gen_wildvar_binding(block var, const char* name, block body) {- return BLOCK(gen_op_simple(DUP), var,- block_bind(gen_op_unbound(STOREV, name),- body, OP_HAS_VARIABLE | OP_BIND_WILDCARD));-}--block gen_cond(block cond, block iftrue, block iffalse) {- return BLOCK(gen_op_simple(DUP), cond,- gen_condbranch(BLOCK(gen_op_simple(POP), iftrue),- BLOCK(gen_op_simple(POP), iffalse)));-}--block gen_try_handler(block handler) {- // Quite a pain just to hide jq's internal errors.- return gen_cond(// `if type=="object" and .__jq- gen_and(gen_call("_equal",- BLOCK(gen_lambda(gen_const(jv_string("object"))),- gen_lambda(gen_noop()))),- BLOCK(gen_subexp(gen_const(jv_string("__jq"))),- gen_noop(),- gen_op_simple(INDEX))),- // `then error`- gen_call("error", gen_noop()),- // `else HANDLER end`- handler);-}--block gen_try(block exp, block handler) {- /*- * Produce something like:- * FORK_OPT <address of handler>- * <exp>- * JUMP <end of handler>- * <handler>- *- * If this is not an internal try/catch, then catch and re-raise- * internal errors to prevent them from leaking.- *- * The handler will only execute if we backtrack to the FORK_OPT with- * an error (exception). If <exp> produces no value then FORK_OPT- * will backtrack (propagate the `empty`, as it were. If <exp>- * produces a value then we'll execute whatever bytecode follows this- * sequence.- */- if (!handler.first && !handler.last)- // A hack to deal with `.` as the handler; we could use a real NOOP here- handler = BLOCK(gen_op_simple(DUP), gen_op_simple(POP), handler);- exp = BLOCK(exp, gen_op_target(JUMP, handler));- return BLOCK(gen_op_target(FORK_OPT, exp), exp, handler);-}--block gen_label(const char *label, block exp) {- block cond = gen_call("_equal",- BLOCK(gen_lambda(gen_noop()),- gen_lambda(gen_op_unbound(LOADV, label))));- return gen_wildvar_binding(gen_op_simple(GENLABEL), label,- BLOCK(gen_op_simple(POP),- // try exp catch if . == $label- // then empty- // else error end- //- // Can't use gen_binop(), as that's firmly- // stuck in parser.y as it refers to things- // like EQ.- gen_try(exp,- gen_cond(cond,- gen_op_simple(BACKTRACK),- gen_call("error", gen_noop())))));-}--block gen_cbinding(const struct cfunction* cfunctions, int ncfunctions, block code) {- for (int cfunc=0; cfunc<ncfunctions; cfunc++) {- inst* i = inst_new(CLOSURE_CREATE_C);- i->imm.cfunc = &cfunctions[cfunc];- i->symbol = strdup(i->imm.cfunc->name);- code = block_bind(inst_block(i), code, OP_IS_CALL_PSEUDO);- }- return code;-}--static uint16_t nesting_level(struct bytecode* bc, inst* target) {- uint16_t level = 0;- assert(bc && target && target->compiled);- while (bc && target->compiled != bc) {- level++;- bc = bc->parent;- }- assert(bc && bc == target->compiled);- return level;-}--static int count_cfunctions(block b) {- int n = 0;- for (inst* i = b.first; i; i = i->next) {- if (i->op == CLOSURE_CREATE_C) n++;- n += count_cfunctions(i->subfn);- }- return n;-}---// Expands call instructions into a calling sequence-static int expand_call_arglist(block* b) {- int errors = 0;- block ret = gen_noop();- for (inst* curr; (curr = block_take(b));) {- if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {- if (!curr->bound_by) {- if (curr->symbol[0] == '*' && curr->symbol[1] >= '1' && curr->symbol[1] <= '3' && curr->symbol[2] == '\0')- locfile_locate(curr->locfile, curr->source, "jq: error: break used outside labeled control structure");- else- locfile_locate(curr->locfile, curr->source, "jq: error: %s/%d is not defined", curr->symbol, block_count_actuals(curr->arglist));- errors++;- // don't process this instruction if it's not well-defined- ret = BLOCK(ret, inst_block(curr));- continue;- }- }-- block prelude = gen_noop();- if (curr->op == CALL_JQ) {- int actual_args = 0, desired_args = 0;- // We expand the argument list as a series of instructions- switch (curr->bound_by->op) {- default: assert(0 && "Unknown function type"); break;- case CLOSURE_CREATE:- case CLOSURE_PARAM: {- block callargs = gen_noop();- for (inst* i; (i = block_take(&curr->arglist));) {- assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO);- block b = inst_block(i);- switch (i->op) {- default: assert(0 && "Unknown type of parameter"); break;- case CLOSURE_REF:- block_append(&callargs, b);- break;- case CLOSURE_CREATE:- block_append(&prelude, b);- block_append(&callargs, gen_op_bound(CLOSURE_REF, b));- break;- }- actual_args++;- }- curr->imm.intval = actual_args;- curr->arglist = callargs;-- if (curr->bound_by->op == CLOSURE_CREATE) {- for (inst* i = curr->bound_by->arglist.first; i; i = i->next) {- assert(i->op == CLOSURE_PARAM);- desired_args++;- }- }- break;- }-- case CLOSURE_CREATE_C: {- for (inst* i; (i = block_take(&curr->arglist)); ) {- assert(i->op == CLOSURE_CREATE); // FIXME- block body = i->subfn;- i->subfn = gen_noop();- inst_free(i);- // arguments should be pushed in reverse order, prepend them to prelude- errors += expand_call_arglist(&body);- prelude = BLOCK(gen_subexp(body), prelude);- actual_args++;- }- assert(curr->op == CALL_JQ);- curr->op = CALL_BUILTIN;- curr->imm.intval = actual_args + 1 /* include the implicit input in arg count */;- assert(curr->bound_by->op == CLOSURE_CREATE_C);- desired_args = curr->bound_by->imm.cfunc->nargs - 1;- assert(!curr->arglist.first);- break;- }- }-- assert(actual_args == desired_args); // because now handle this above- }- ret = BLOCK(ret, prelude, inst_block(curr));- }- *b = ret;- return errors;-}--static int compile(struct bytecode* bc, block b, struct locfile* lf) {- int errors = 0;- int pos = 0;- int var_frame_idx = 0;- bc->nsubfunctions = 0;- errors += expand_call_arglist(&b);- b = BLOCK(b, gen_op_simple(RET));- jv localnames = jv_array();- for (inst* curr = b.first; curr; curr = curr->next) {- if (!curr->next) assert(curr == b.last);- int length = opcode_describe(curr->op)->length;- if (curr->op == CALL_JQ) {- for (inst* arg = curr->arglist.first; arg; arg = arg->next) {- length += 2;- }- }- pos += length;- curr->bytecode_pos = pos;- curr->compiled = bc;-- assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);-- if ((opcode_describe(curr->op)->flags & OP_HAS_VARIABLE) &&- curr->bound_by == curr) {- curr->imm.intval = var_frame_idx++;- localnames = jv_array_append(localnames, jv_string(curr->symbol));- }-- if (curr->op == CLOSURE_CREATE) {- assert(curr->bound_by == curr);- curr->imm.intval = bc->nsubfunctions++;- }- if (curr->op == CLOSURE_CREATE_C) {- assert(curr->bound_by == curr);- int idx = bc->globals->ncfunctions++;- bc->globals->cfunc_names = jv_array_append(bc->globals->cfunc_names,- jv_string(curr->symbol));- bc->globals->cfunctions[idx] = *curr->imm.cfunc;- curr->imm.intval = idx;- }- }- if (pos > 0xFFFF) {- // too long for program counter to fit in uint16_t- locfile_locate(lf, UNKNOWN_LOCATION,- "function compiled to %d bytes which is too long", pos);- errors++;- }- bc->codelen = pos;- bc->debuginfo = jv_object_set(bc->debuginfo, jv_string("locals"), localnames);- if (bc->nsubfunctions) {- bc->subfunctions = jv_mem_alloc(sizeof(struct bytecode*) * bc->nsubfunctions);- for (inst* curr = b.first; curr; curr = curr->next) {- if (curr->op == CLOSURE_CREATE) {- struct bytecode* subfn = jv_mem_alloc(sizeof(struct bytecode));- bc->subfunctions[curr->imm.intval] = subfn;- subfn->globals = bc->globals;- subfn->parent = bc;- subfn->nclosures = 0;- subfn->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_string(curr->symbol));- jv params = jv_array();- for (inst* param = curr->arglist.first; param; param = param->next) {- assert(param->op == CLOSURE_PARAM);- assert(param->bound_by == param);- param->imm.intval = subfn->nclosures++;- param->compiled = subfn;- params = jv_array_append(params, jv_string(param->symbol));- }- subfn->debuginfo = jv_object_set(subfn->debuginfo, jv_string("params"), params);- errors += compile(subfn, curr->subfn, lf);- curr->subfn = gen_noop();- }- }- } else {- bc->subfunctions = 0;- }- uint16_t* code = jv_mem_alloc(sizeof(uint16_t) * bc->codelen);- bc->code = code;- pos = 0;- jv constant_pool = jv_array();- int maxvar = -1;- if (!errors) for (inst* curr = b.first; curr; curr = curr->next) {- const struct opcode_description* op = opcode_describe(curr->op);- if (op->length == 0)- continue;- code[pos++] = curr->op;- assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);- if (curr->op == CALL_BUILTIN) {- assert(curr->bound_by->op == CLOSURE_CREATE_C);- assert(!curr->arglist.first);- code[pos++] = (uint16_t)curr->imm.intval;- code[pos++] = curr->bound_by->imm.intval;- } else if (curr->op == CALL_JQ) {- assert(curr->bound_by->op == CLOSURE_CREATE ||- curr->bound_by->op == CLOSURE_PARAM);- code[pos++] = (uint16_t)curr->imm.intval;- code[pos++] = nesting_level(bc, curr->bound_by);- code[pos++] = curr->bound_by->imm.intval |- (curr->bound_by->op == CLOSURE_CREATE ? ARG_NEWCLOSURE : 0);- for (inst* arg = curr->arglist.first; arg; arg = arg->next) {- assert(arg->op == CLOSURE_REF && arg->bound_by->op == CLOSURE_CREATE);- code[pos++] = nesting_level(bc, arg->bound_by);- code[pos++] = arg->bound_by->imm.intval | ARG_NEWCLOSURE;- }- } else if ((op->flags & OP_HAS_CONSTANT) && (op->flags & OP_HAS_VARIABLE)) {- // STORE_GLOBAL: constant global, basically- code[pos++] = jv_array_length(jv_copy(constant_pool));- constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));- code[pos++] = nesting_level(bc, curr->bound_by);- uint16_t var = (uint16_t)curr->bound_by->imm.intval;- code[pos++] = var;- } else if (op->flags & OP_HAS_CONSTANT) {- code[pos++] = jv_array_length(jv_copy(constant_pool));- constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));- } else if (op->flags & OP_HAS_VARIABLE) {- code[pos++] = nesting_level(bc, curr->bound_by);- uint16_t var = (uint16_t)curr->bound_by->imm.intval;- code[pos++] = var;- if (var > maxvar) maxvar = var;- } else if (op->flags & OP_HAS_BRANCH) {- assert(curr->imm.target->bytecode_pos != -1);- assert(curr->imm.target->bytecode_pos > pos); // only forward branches- code[pos] = curr->imm.target->bytecode_pos - (pos + 1);- pos++;- } else if (op->length > 1) {- assert(0 && "codegen not implemented for this operation");- }- }- bc->constants = constant_pool;- bc->nlocals = maxvar + 2; // FIXME: frames of size zero?- block_free(b);- return errors;-}--int block_compile(block b, struct bytecode** out, struct locfile* lf) {- struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode));- bc->parent = 0;- bc->nclosures = 0;- bc->globals = jv_mem_alloc(sizeof(struct symbol_table));- int ncfunc = count_cfunctions(b);- bc->globals->ncfunctions = 0;- bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc);- bc->globals->cfunc_names = jv_array();- bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());- int nerrors = compile(bc, b, lf);- assert(bc->globals->ncfunctions == ncfunc);- if (nerrors > 0) {- bytecode_free(bc);- *out = 0;- } else {- *out = bc;- }- return nerrors;-}--void block_free(block b) {- struct inst* next;- for (struct inst* curr = b.first; curr; curr = next) {- next = curr->next;- inst_free(curr);- }-}
compile.h
@@ -1,102 +0,0 @@-#ifndef COMPILE_H-#define COMPILE_H-#include <stdint.h>-#include "jv.h"-#include "bytecode.h"-#include "locfile.h"--struct inst;-typedef struct inst inst;---typedef struct block {- inst* first;- inst* last;-} block;--block gen_location(location, struct locfile*, block);--block gen_noop();-int block_is_noop(block b);-block gen_op_simple(opcode op);-block gen_const(jv constant);-block gen_const_global(jv constant, const char *name);-int block_is_const(block b);-int block_is_const_inf(block b);-jv_kind block_const_kind(block b);-jv block_const(block b);-block gen_op_target(opcode op, block target);-block gen_op_unbound(opcode op, const char* name);-block gen_op_bound(opcode op, block binder);-block gen_op_var_fresh(opcode op, const char* name);--block gen_module(block metadata);-jv block_module_meta(block b);-block gen_import(const char* name, const char *as, int is_data);-block gen_import_meta(block import, block metadata);-block gen_function(const char* name, block formals, block body);-block gen_param_regular(const char* name);-block gen_param(const char* name);-block gen_lambda(block body);-block gen_call(const char* name, block body);-block gen_subexp(block a);-block gen_both(block a, block b);-block gen_const_object(block expr);-block gen_collect(block expr);-block gen_reduce(block source, block matcher, block init, block body);-block gen_foreach(block source, block matcher, block init, block update, block extract);-block gen_definedor(block a, block b);-block gen_condbranch(block iftrue, block iffalse);-block gen_and(block a, block b);-block gen_or(block a, block b);--block gen_var_binding(block var, const char* name, block body);-block gen_array_matcher(block left, block curr);-block gen_object_matcher(block name, block curr);-block gen_destructure(block var, block matcher, block body);--block gen_cond(block cond, block iftrue, block iffalse);-block gen_try_handler(block handler);-block gen_try(block exp, block handler);-block gen_label(const char *label, block exp);--block gen_cbinding(const struct cfunction* functions, int nfunctions, block b);--void block_append(block* b, block b2);-block block_join(block a, block b);-int block_has_only_binders_and_imports(block, int bindflags);-int block_has_only_binders(block, int bindflags);-int block_has_main(block);-int block_is_funcdef(block b);-int block_is_single(block b);-block block_bind(block binder, block body, int bindflags);-block block_bind_library(block binder, block body, int bindflags, const char* libname);-block block_bind_referenced(block binder, block body, int bindflags);-block block_drop_unreferenced(block body);--jv block_take_imports(block* body);--int block_compile(block, struct bytecode**, struct locfile*);--void block_free(block);----// Here's some horrible preprocessor gunk so that code-// sequences can be contructed as BLOCK(block1, block2, block3)--#define BLOCK_1(b1) (b1)-#define BLOCK_2(b1,b2) (block_join((b1),(b2)))-#define BLOCK_3(b1,b2,b3) (block_join(BLOCK_2(b1,b2),(b3)))-#define BLOCK_4(b1,b2,b3,b4) (block_join(BLOCK_3(b1,b2,b3),(b4)))-#define BLOCK_5(b1,b2,b3,b4,b5) (block_join(BLOCK_4(b1,b2,b3,b4),(b5)))-#define BLOCK_6(b1,b2,b3,b4,b5,b6) (block_join(BLOCK_5(b1,b2,b3,b4,b5),(b6)))-#define BLOCK_7(b1,b2,b3,b4,b5,b6,b7) (block_join(BLOCK_6(b1,b2,b3,b4,b5,b6),(b7)))-#define BLOCK_8(b1,b2,b3,b4,b5,b6,b7,b8) (block_join(BLOCK_7(b1,b2,b3,b4,b5,b6,b7),(b8)))--#define BLOCK_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME-#define BLOCK(...) \- BLOCK_IDX(__VA_ARGS__, BLOCK_8, BLOCK_7, BLOCK_6, BLOCK_5, BLOCK_4, BLOCK_3, BLOCK_2, BLOCK_1)(__VA_ARGS__)---#endif
@@ -9,7 +9,7 @@ m4_include([m4/ax_prog_bison_version.m4]) dnl Created autoconf implementation thompson@dtosolutions, 26NOV12 AC_PREREQ([2.64]) AC_CONFIG_AUX_DIR([config])-AM_INIT_AUTOMAKE([1.11.2 parallel-tests foreign -Wall])+AM_INIT_AUTOMAKE([1.11.2 subdir-objects parallel-tests foreign -Wall]) AM_SILENT_RULES([yes]) AM_PROG_AR AM_MAINTAINER_MODE([enable])
exec_stack.h
@@ -1,112 +0,0 @@-#ifndef EXEC_STACK_H-#define EXEC_STACK_H-#include <stddef.h>-#include <stdint.h>-#include <stdlib.h>-#include <string.h>-#include "jv_alloc.h"--/*- * The stack is a directed forest of variably sized blocks. Each block has a- * "next" block which is at a higher memory address, or 0 if the block has no- * "next" block. More than one block may have no "next" block. A block may be- * the "next" block of more than one other block. Pushed blocks are added at- * the low-address end of the stack.- *- * Stack pointers are negative integers that are offsets relative to "mem_end",- * the end of the allocated region. The stack "bound" is the stack pointer of- * the last block that would be able to fit in the currently allocated region.- * The stack "limit" is the stack pointer of the last block currently in the- * stack. The stack pointer of the "next" block is stored directly below each- * block.- *- * <- mem_end = 0x100- * 0xF8 +------------+- * 0xF0 | |- * 0xE8 +------------+ <- stack_ptr1 = -0x18- * 0xE0 next = 0- * 0xD8 +------------+- * 0xD0 | |- * 0xC8 | |- * 0xC0 +------------+ <- stack_ptr2 = limit = -0x40- * 0xB8 next = -0x18- * 0xB0- * 0xA8 <- bound = -0x58- * 0xA0- */--struct determine_alignment {- char x;- union { int i; double d; uint64_t u64; size_t sz; void* ptr; } u;-};-enum {ALIGNMENT = offsetof(struct determine_alignment, u)};--static size_t align_round_up(size_t sz) {- return ((sz + (ALIGNMENT - 1)) / ALIGNMENT) * ALIGNMENT;-}--typedef int stack_ptr;--struct stack {- char* mem_end; // one-past-the-end of allocated region- stack_ptr bound;- stack_ptr limit; // 0 - stack is empty-};--static void stack_init(struct stack* s) {- s->mem_end = 0;- s->bound = ALIGNMENT;- s->limit = 0;-}--static void stack_reset(struct stack* s) {- assert(s->limit == 0 && "stack freed while not empty");- char* mem_start = s->mem_end - ( -s->bound + ALIGNMENT);- free(mem_start);- stack_init(s);-}--static int stack_pop_will_free(struct stack* s, stack_ptr p) {- return p == s->limit;-}--static void* stack_block(struct stack* s, stack_ptr p) {- return (void*)(s->mem_end + p);-}--static stack_ptr* stack_block_next(struct stack* s, stack_ptr p) {- return &((stack_ptr*)stack_block(s, p))[-1];-}--static void stack_reallocate(struct stack* s, size_t sz) {- int old_mem_length = -(s->bound) + ALIGNMENT;- char* old_mem_start = s->mem_end - old_mem_length;-- int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2);- char* new_mem_start = jv_mem_realloc(old_mem_start, new_mem_length);- memmove(new_mem_start + (new_mem_length - old_mem_length),- new_mem_start, old_mem_length);- s->mem_end = new_mem_start + new_mem_length;- s->bound = -(new_mem_length - ALIGNMENT);-}--static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) {- int alloc_sz = align_round_up(sz) + ALIGNMENT;- stack_ptr r = s->limit - alloc_sz;- if (r < s->bound) {- stack_reallocate(s, alloc_sz);- }- s->limit = r;- *stack_block_next(s, r) = p;- return r;-}--static stack_ptr stack_pop_block(struct stack* s, stack_ptr p, size_t sz) {- stack_ptr r = *stack_block_next(s, p);- if (p == s->limit) {- int alloc_sz = align_round_up(sz) + ALIGNMENT;- s->limit += alloc_sz;- }- return r;-}-#endif
execute.c
@@ -1,1155 +0,0 @@-#include <assert.h>-#include <errno.h>-#include <stdarg.h>-#include <stdio.h>-#include <stdlib.h>-#include <stdint.h>-#include <sys/stat.h>--#include "exec_stack.h"-#include "bytecode.h"--#include "jv_alloc.h"-#include "jq_parser.h"-#include "locfile.h"-#include "jv.h"-#include "jq.h"-#include "parser.h"-#include "builtin.h"-#include "util.h"-#include "linker.h"--struct jq_state {- void (*nomem_handler)(void *);- void *nomem_handler_data;- struct bytecode* bc;-- jq_msg_cb err_cb;- void *err_cb_data;- jv error;-- struct stack stk;- stack_ptr curr_frame;- stack_ptr stk_top;- stack_ptr fork_top;-- jv path;- jv value_at_path;- int subexp_nest;- int debug_trace_enabled;- int initial_execution;- unsigned next_label;-- jv attrs;- jq_input_cb input_cb;- void *input_cb_data;- jq_msg_cb debug_cb;- void *debug_cb_data;-};--struct closure {- struct bytecode* bc; // jq bytecode- stack_ptr env; // jq stack address of closed frame-};--// locals for any function called: either a closure or a local variable-union frame_entry {- struct closure closure;- jv localvar;-};--// jq function call frame-struct frame {- struct bytecode* bc; // jq bytecode for callee- stack_ptr env; // jq stack address of frame to return to- stack_ptr retdata; // jq stack address to unwind to on RET- uint16_t* retaddr; // jq bytecode return address- union frame_entry entries[]; // nclosures + nlocals-};--static int frame_size(struct bytecode* bc) {- return sizeof(struct frame) + sizeof(union frame_entry) * (bc->nclosures + bc->nlocals);-}--static struct frame* frame_current(struct jq_state* jq) {- struct frame* fp = stack_block(&jq->stk, jq->curr_frame);-- stack_ptr next = *stack_block_next(&jq->stk, jq->curr_frame);- if (next) {- struct frame* fpnext = stack_block(&jq->stk, next);- struct bytecode* bc = fpnext->bc;- assert(fp->retaddr >= bc->code && fp->retaddr < bc->code + bc->codelen);- } else {- assert(fp->retaddr == 0);- }- return fp;-}--static stack_ptr frame_get_level(struct jq_state* jq, int level) {- stack_ptr fr = jq->curr_frame;- for (int i=0; i<level; i++) {- struct frame* fp = stack_block(&jq->stk, fr);- fr = fp->env;- }- return fr;-}--static jv* frame_local_var(struct jq_state* jq, int var, int level) {- struct frame* fr = stack_block(&jq->stk, frame_get_level(jq, level));- assert(var >= 0);- assert(var < fr->bc->nlocals);- return &fr->entries[fr->bc->nclosures + var].localvar;-}--static struct closure make_closure(struct jq_state* jq, uint16_t* pc) {- uint16_t level = *pc++;- uint16_t idx = *pc++;- stack_ptr fridx = frame_get_level(jq, level);- struct frame* fr = stack_block(&jq->stk, fridx);- if (idx & ARG_NEWCLOSURE) {- // A new closure closing the frame identified by level, and with- // the bytecode body of the idx'th subfunction of that frame- int subfn_idx = idx & ~ARG_NEWCLOSURE;- assert(subfn_idx < fr->bc->nsubfunctions);- struct closure cl = {fr->bc->subfunctions[subfn_idx],- fridx};- return cl;- } else {- // A reference to a closure from the frame identified by level; copy- // it as-is- int closure = idx;- assert(closure >= 0);- assert(closure < fr->bc->nclosures);- return fr->entries[closure].closure;- }-}--static struct frame* frame_push(struct jq_state* jq, struct closure callee,- uint16_t* argdef, int nargs) {- stack_ptr new_frame_idx = stack_push_block(&jq->stk, jq->curr_frame, frame_size(callee.bc));- struct frame* new_frame = stack_block(&jq->stk, new_frame_idx);- new_frame->bc = callee.bc;- new_frame->env = callee.env;- assert(nargs == new_frame->bc->nclosures);- union frame_entry* entries = new_frame->entries;- for (int i=0; i<nargs; i++) {- entries->closure = make_closure(jq, argdef + i * 2);- entries++;- }- for (int i=0; i<callee.bc->nlocals; i++) {- entries->localvar = jv_invalid();- entries++;- }- jq->curr_frame = new_frame_idx;- return new_frame;-}--static void frame_pop(struct jq_state* jq) {- assert(jq->curr_frame);- struct frame* fp = frame_current(jq);- if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {- int nlocals = fp->bc->nlocals;- for (int i=0; i<nlocals; i++) {- jv_free(*frame_local_var(jq, i, 0));- }- }- jq->curr_frame = stack_pop_block(&jq->stk, jq->curr_frame, frame_size(fp->bc));-}--void stack_push(jq_state *jq, jv val) {- assert(jv_is_valid(val));- jq->stk_top = stack_push_block(&jq->stk, jq->stk_top, sizeof(jv));- jv* sval = stack_block(&jq->stk, jq->stk_top);- *sval = val;-}--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;-}--// Like stack_pop(), but assert !stack_pop_will_free() and replace with-// jv_null() on the stack.-jv stack_popn(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)) {- *sval = jv_null();- }- jq->stk_top = stack_pop_block(&jq->stk, jq->stk_top, sizeof(jv));- assert(jv_is_valid(val));- return val;-}---struct forkpoint {- stack_ptr saved_data_stack;- stack_ptr saved_curr_frame;- int path_len, subexp_nest;- jv value_at_path;- uint16_t* return_address;-};--struct stack_pos {- stack_ptr saved_data_stack, saved_curr_frame;-};--struct stack_pos stack_get_pos(jq_state* jq) {- struct stack_pos sp = {jq->stk_top, jq->curr_frame};- return sp;-}--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->value_at_path = jv_copy(jq->value_at_path);- 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 int path_intact(jq_state *jq, jv curr) {- if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {- return jv_identical(curr, jv_copy(jq->value_at_path));- } else {- jv_free(curr);- return 1;- }-}--static void path_append(jq_state* jq, jv component, jv value_at_path) {- if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {- int n1 = jv_array_length(jv_copy(jq->path));- jq->path = jv_array_append(jq->path, component);- int n2 = jv_array_length(jv_copy(jq->path));- assert(n2 == n1 + 1);- jv_free(jq->value_at_path);- jq->value_at_path = value_at_path;- } else {- jv_free(component);- jv_free(value_at_path);- }-}--uint16_t* stack_restore(jq_state *jq){- while (!stack_pop_will_free(&jq->stk, jq->fork_top)) {- if (stack_pop_will_free(&jq->stk, jq->stk_top)) {- jv_free(stack_pop(jq));- } else if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {- frame_pop(jq);- } else {- assert(0);- }- }-- if (jq->fork_top == 0) {- return 0;- }-- struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);- uint16_t* retaddr = fork->return_address;- jq->stk_top = fork->saved_data_stack;- jq->curr_frame = fork->saved_curr_frame;- int path_len = fork->path_len;- if (jv_get_kind(jq->path) == JV_KIND_ARRAY) {- assert(path_len >= 0);- jq->path = jv_array_slice(jq->path, 0, path_len);- } else {- assert(path_len == 0);- }- jv_free(jq->value_at_path);- jq->value_at_path = fork->value_at_path;- jq->subexp_nest = fork->subexp_nest;- jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));- return retaddr;-}--static void jq_reset(jq_state *jq) {- while (stack_restore(jq)) {}-- assert(jq->stk_top == 0);- assert(jq->fork_top == 0);- assert(jq->curr_frame == 0);- stack_reset(&jq->stk);- jv_free(jq->error);- jq->error = jv_null();-- if (jv_get_kind(jq->path) != JV_KIND_INVALID)- jv_free(jq->path);- jq->path = jv_null();- jv_free(jq->value_at_path);- jq->value_at_path = jv_null();- jq->subexp_nest = 0;-}--void jq_report_error(jq_state *jq, jv value) {- assert(jq->err_cb);- // callback must jv_free() its jv argument- jq->err_cb(jq->err_cb_data, value);-}--static void set_error(jq_state *jq, jv value) {- // Record so try/catch can find it.- jv_free(jq->error);- jq->error = value;-}--#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)--jv jq_next(jq_state *jq) {- jv cfunc_input[MAX_CFUNCTION_ARGS];-- jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);-- uint16_t* pc = stack_restore(jq);- assert(pc);-- int raising;- int backtracking = !jq->initial_execution;- jq->initial_execution = 0;- assert(jv_get_kind(jq->error) == JV_KIND_NULL);- while (1) {- uint16_t opcode = *pc;- raising = 0;-- if (jq->debug_trace_enabled) {- dump_operation(frame_current(jq)->bc, pc);- printf("\t");- const struct opcode_description* opdesc = opcode_describe(opcode);- stack_ptr param = 0;- if (!backtracking) {- int stack_in = opdesc->stack_in;- if (stack_in == -1) stack_in = pc[1];- for (int i=0; i<stack_in; i++) {- if (i == 0) {- param = jq->stk_top;- } else {- printf(" | ");- param = *stack_block_next(&jq->stk, param);- }- if (!param) break;- jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), JV_PRINT_REFCOUNT);- //printf("<%d>", jv_get_refcnt(param->val));- //printf(" -- ");- //jv_dump(jv_copy(jq->path), 0);- }- } else {- printf("\t<backtracking>");- }-- printf("\n");- }-- if (backtracking) {- opcode = ON_BACKTRACK(opcode);- backtracking = 0;- raising = !jv_is_valid(jq->error);- }- pc++;-- switch (opcode) {- default: assert(0 && "invalid instruction");-- case TOP: break;-- case LOADK: {- jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);- assert(jv_is_valid(v));- jv_free(stack_pop(jq));- stack_push(jq, v);- break;- }-- case GENLABEL: {- stack_push(jq, JV_OBJECT(jv_string("__jq"), jv_number(jq->next_label++)));- break;- }-- case DUP: {- jv v = stack_pop(jq);- stack_push(jq, jv_copy(v));- stack_push(jq, v);- break;- }-- case DUPN: {- jv v = stack_popn(jq);- stack_push(jq, jv_copy(v));- stack_push(jq, v);- break;- }-- case DUP2: {- jv keep = stack_pop(jq);- jv v = stack_pop(jq);- stack_push(jq, jv_copy(v));- stack_push(jq, keep);- stack_push(jq, v);- break;- }-- case SUBEXP_BEGIN: {- jv v = stack_pop(jq);- stack_push(jq, jv_copy(v));- stack_push(jq, v);- jq->subexp_nest++;- break;- }-- case SUBEXP_END: {- assert(jq->subexp_nest > 0);- jq->subexp_nest--;- jv a = stack_pop(jq);- jv b = stack_pop(jq);- stack_push(jq, a);- stack_push(jq, b);- break;- }-- case POP: {- jv_free(stack_pop(jq));- break;- }-- case APPEND: {- jv v = stack_pop(jq);- uint16_t level = *pc++;- uint16_t vidx = *pc++;- jv* var = frame_local_var(jq, vidx, level);- assert(jv_get_kind(*var) == JV_KIND_ARRAY);- *var = jv_array_append(*var, v);- break;- }-- case INSERT: {- jv stktop = stack_pop(jq);- jv v = stack_pop(jq);- jv k = stack_pop(jq);- jv objv = stack_pop(jq);- assert(jv_get_kind(objv) == JV_KIND_OBJECT);- if (jv_get_kind(k) == JV_KIND_STRING) {- stack_push(jq, jv_object_set(objv, k, v));- stack_push(jq, stktop);- } else {- char errbuf[15];- set_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s (%s) as object key",- jv_kind_name(jv_get_kind(k)),- jv_dump_string_trunc(jv_copy(k), errbuf, sizeof(errbuf)))));- jv_free(stktop);- jv_free(v);- jv_free(k);- jv_free(objv);- goto do_backtrack;- }- break;- }-- case ON_BACKTRACK(RANGE):- case RANGE: {- uint16_t level = *pc++;- uint16_t v = *pc++;- jv* var = frame_local_var(jq, v, level);- jv max = stack_pop(jq);- if (raising) 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))) {- /* finished iterating */- goto do_backtrack;- } else {- jv curr = jv_copy(*var);- *var = jv_number(jv_number_value(*var) + 1);-- struct stack_pos spos = stack_get_pos(jq);- stack_push(jq, jv_copy(max));- stack_save(jq, pc - 3, spos);-- stack_push(jq, curr);- }- break;- }-- // FIXME: loadv/storev may do too much copying/freeing- case LOADV: {- uint16_t level = *pc++;- uint16_t v = *pc++;- jv* var = frame_local_var(jq, v, level);- if (jq->debug_trace_enabled) {- printf("V%d = ", v);- jv_dump(jv_copy(*var), 0);- printf(" (%d)\n", jv_get_refcnt(*var));- }- jv_free(stack_pop(jq));- stack_push(jq, jv_copy(*var));- break;- }-- // Does a load but replaces the variable with null- case LOADVN: {- uint16_t level = *pc++;- uint16_t v = *pc++;- jv* var = frame_local_var(jq, v, level);- if (jq->debug_trace_enabled) {- printf("V%d = ", v);- jv_dump(jv_copy(*var), 0);- printf(" (%d)\n", jv_get_refcnt(*var));- }- jv_free(stack_popn(jq));- stack_push(jq, *var);- *var = jv_null();- break;- }-- case STOREV: {- uint16_t level = *pc++;- uint16_t v = *pc++;- jv* var = frame_local_var(jq, v, level);- jv val = stack_pop(jq);- if (jq->debug_trace_enabled) {- printf("V%d = ", v);- jv_dump(jv_copy(val), 0);- printf(" (%d)\n", jv_get_refcnt(val));- }- jv_free(*var);- *var = val;- break;- }-- case STORE_GLOBAL: {- // Get the constant- jv val = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);- assert(jv_is_valid(val));-- // Store the var- uint16_t level = *pc++;- uint16_t v = *pc++;- jv* var = frame_local_var(jq, v, level);- if (jq->debug_trace_enabled) {- printf("V%d = ", v);- jv_dump(jv_copy(val), 0);- printf(" (%d)\n", jv_get_refcnt(val));- }- jv_free(*var);- *var = val;- break;- }-- case PATH_BEGIN: {- jv v = stack_pop(jq);- stack_push(jq, jq->path);-- stack_save(jq, pc - 1, stack_get_pos(jq));-- stack_push(jq, jv_number(jq->subexp_nest));- stack_push(jq, jq->value_at_path);- stack_push(jq, jv_copy(v));-- jq->path = jv_array();- jq->value_at_path = v; // next INDEX operation must index into v- jq->subexp_nest = 0;- break;- }-- case PATH_END: {- jv v = stack_pop(jq);- // detect invalid path expression like path(.a | reverse)- if (!path_intact(jq, jv_copy(v))) {- char errbuf[30];- jv msg = jv_string_fmt(- "Invalid path expression with result %s",- jv_dump_string_trunc(v, errbuf, sizeof(errbuf)));- set_error(jq, jv_invalid_with_msg(msg));- goto do_backtrack;- }- jv_free(v); // discard value, only keep path-- jv old_value_at_path = stack_pop(jq);- int old_subexp_nest = (int)jv_number_value(stack_pop(jq));-- jv path = jq->path;- jq->path = stack_pop(jq);-- struct stack_pos spos = stack_get_pos(jq);- stack_push(jq, jv_copy(path));- stack_save(jq, pc - 1, spos);-- stack_push(jq, path);- jq->subexp_nest = old_subexp_nest;- jv_free(jq->value_at_path);- jq->value_at_path = old_value_at_path;- break;- }-- case ON_BACKTRACK(PATH_BEGIN):- case ON_BACKTRACK(PATH_END): {- jv_free(jq->path);- jq->path = stack_pop(jq);- goto do_backtrack;- }-- case INDEX:- case INDEX_OPT: {- jv t = stack_pop(jq);- jv k = stack_pop(jq);- // detect invalid path expression like path(reverse | .a)- if (!path_intact(jq, jv_copy(t))) {- char keybuf[15];- char objbuf[30];- jv msg = jv_string_fmt(- "Invalid path expression near attempt to access element %s of %s",- jv_dump_string_trunc(k, keybuf, sizeof(keybuf)),- jv_dump_string_trunc(t, objbuf, sizeof(objbuf)));- set_error(jq, jv_invalid_with_msg(msg));- goto do_backtrack;- }- jv v = jv_get(t, jv_copy(k));- if (jv_is_valid(v)) {- path_append(jq, k, jv_copy(v));- stack_push(jq, v);- } else {- jv_free(k);- if (opcode == INDEX)- set_error(jq, v);- else- jv_free(v);- goto do_backtrack;- }- break;- }--- case JUMP: {- uint16_t offset = *pc++;- pc += offset;- break;- }-- case JUMP_F: {- uint16_t offset = *pc++;- jv t = stack_pop(jq);- jv_kind kind = jv_get_kind(t);- if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {- pc += offset;- }- stack_push(jq, t); // FIXME do this better- break;- }-- case EACH:- case EACH_OPT: {- jv container = stack_pop(jq);- // detect invalid path expression like path(reverse | .[])- if (!path_intact(jq, jv_copy(container))) {- char errbuf[30];- jv msg = jv_string_fmt(- "Invalid path expression near attempt to iterate through %s",- jv_dump_string_trunc(container, errbuf, sizeof(errbuf)));- set_error(jq, jv_invalid_with_msg(msg));- goto do_backtrack;- }- stack_push(jq, container);- stack_push(jq, jv_number(-1));- // fallthrough- }- case ON_BACKTRACK(EACH):- case ON_BACKTRACK(EACH_OPT): {- int idx = jv_number_value(stack_pop(jq));- jv container = stack_pop(jq);-- int keep_going, is_last = 0;- jv key, value;- if (jv_get_kind(container) == JV_KIND_ARRAY) {- if (opcode == EACH || opcode == EACH_OPT) idx = 0;- else idx = idx + 1;- int len = jv_array_length(jv_copy(container));- keep_going = idx < len;- is_last = idx == len - 1;- if (keep_going) {- key = jv_number(idx);- value = jv_array_get(jv_copy(container), idx);- }- } else if (jv_get_kind(container) == JV_KIND_OBJECT) {- if (opcode == EACH || opcode == EACH_OPT) idx = jv_object_iter(container);- else idx = jv_object_iter_next(container, idx);- keep_going = jv_object_iter_valid(container, idx);- if (keep_going) {- key = jv_object_iter_key(container, idx);- value = jv_object_iter_value(container, idx);- }- } else {- assert(opcode == EACH || opcode == EACH_OPT);- if (opcode == EACH) {- char errbuf[15];- set_error(jq,- jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s (%s)",- jv_kind_name(jv_get_kind(container)),- jv_dump_string_trunc(jv_copy(container), errbuf, sizeof(errbuf)))));- }- keep_going = 0;- }-- if (!keep_going || raising) {- if (keep_going)- jv_free(value);- jv_free(container);- goto do_backtrack;- } else if (is_last) {- // we don't need to make a backtrack point- jv_free(container);- path_append(jq, key, jv_copy(value));- stack_push(jq, value);- } else {- struct stack_pos spos = stack_get_pos(jq);- stack_push(jq, container);- stack_push(jq, jv_number(idx));- stack_save(jq, pc - 1, spos);- path_append(jq, key, jv_copy(value));- stack_push(jq, value);- }- break;- }-- do_backtrack:- case BACKTRACK: {- pc = stack_restore(jq);- if (!pc) {- if (!jv_is_valid(jq->error)) {- jv error = jq->error;- jq->error = jv_null();- return error;- }- return jv_invalid();- }- backtracking = 1;- break;- }-- case FORK_OPT:- case FORK: {- stack_save(jq, pc - 1, stack_get_pos(jq));- pc++; // skip offset this time- break;- }-- case ON_BACKTRACK(FORK_OPT): {- if (jv_is_valid(jq->error)) {- // `try EXP ...` backtracked here (no value, `empty`), so we backtrack more- jv_free(stack_pop(jq));- goto do_backtrack;- }- // `try EXP ...` exception caught in EXP- jv_free(stack_pop(jq)); // free the input- stack_push(jq, jv_invalid_get_msg(jq->error)); // push the error's message- jq->error = jv_null();- uint16_t offset = *pc++;- pc += offset;- break;- }- case ON_BACKTRACK(FORK): {- if (raising) goto do_backtrack;- uint16_t offset = *pc++;- pc += offset;- break;- }-- case CALL_BUILTIN: {- int nargs = *pc++;- jv top = stack_pop(jq);- jv* in = cfunc_input;- in[0] = top;- for (int i = 1; i < nargs; i++) {- in[i] = stack_pop(jq);- }- struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++];- typedef jv (*func_1)(jq_state*,jv);- typedef jv (*func_2)(jq_state*,jv,jv);- typedef jv (*func_3)(jq_state*,jv,jv,jv);- typedef jv (*func_4)(jq_state*,jv,jv,jv,jv);- typedef jv (*func_5)(jq_state*,jv,jv,jv,jv,jv);- switch (function->nargs) {- case 1: top = ((func_1)function->fptr)(jq, in[0]); break;- case 2: top = ((func_2)function->fptr)(jq, in[0], in[1]); break;- case 3: top = ((func_3)function->fptr)(jq, in[0], in[1], in[2]); break;- case 4: top = ((func_4)function->fptr)(jq, in[0], in[1], in[2], in[3]); break;- case 5: top = ((func_5)function->fptr)(jq, in[0], in[1], in[2], in[3], in[4]); break;- // FIXME: a) up to 7 arguments (input + 6), b) should assert- // because the compiler should not generate this error.- default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));- }-- if (jv_is_valid(top)) {- stack_push(jq, top);- } else if (jv_invalid_has_msg(jv_copy(top))) {- set_error(jq, top);- goto do_backtrack;- } else {- // C-coded function returns invalid w/o msg? -> backtrack, as if- // it had returned `empty`- goto do_backtrack;- }- break;- }-- case TAIL_CALL_JQ:- case CALL_JQ: {- /*- * Bytecode layout here:- *- * CALL_JQ- * <nclosures> (i.e., number of call arguments)- * <callee closure> (what we're calling)- * <nclosures' worth of closures> (frame reference + code pointer)- *- * <next instruction (to return to)>- *- * Each closure consists of two uint16_t values: a "level"- * identifying the frame to be closed over, and an index.- *- * The level is a relative number of call frames reachable from- * the currently one; 0 -> current frame, 1 -> previous frame, and- * so on.- *- * The index is either an index of the closed frame's subfunctions- * or of the closed frame's parameter closures. If the latter,- * that closure will be passed, else the closed frame's pointer- * and the subfunction's code will form the closure to be passed.- *- * See make_closure() for more information.- */- jv input = stack_pop(jq);- uint16_t nclosures = *pc++;- uint16_t* retaddr = pc + 2 + nclosures*2;- stack_ptr retdata = jq->stk_top;- struct frame* new_frame;- struct closure cl = make_closure(jq, pc);- if (opcode == TAIL_CALL_JQ) {- retaddr = frame_current(jq)->retaddr;- retdata = frame_current(jq)->retdata;- frame_pop(jq);- }- new_frame = frame_push(jq, cl, pc + 2, nclosures);- new_frame->retdata = retdata;- new_frame->retaddr = retaddr;- pc = new_frame->bc->code;- stack_push(jq, input);- break;- }-- case RET: {- jv value = stack_pop(jq);- assert(jq->stk_top == frame_current(jq)->retdata);- uint16_t* retaddr = frame_current(jq)->retaddr;- if (retaddr) {- // function return- pc = retaddr;- frame_pop(jq);- } else {- // top-level return, yielding value- struct stack_pos spos = stack_get_pos(jq);- stack_push(jq, jv_null());- stack_save(jq, pc - 1, spos);- return value;- }- stack_push(jq, value);- break;- }- case ON_BACKTRACK(RET): {- // resumed after top-level return- goto do_backtrack;- }- }- }-}--jv jq_format_error(jv msg) {- if (jv_get_kind(msg) == JV_KIND_NULL ||- (jv_get_kind(msg) == JV_KIND_INVALID && !jv_invalid_has_msg(jv_copy(msg)))) {- jv_free(msg);- fprintf(stderr, "jq: error: out of memory\n");- return jv_null();- }-- if (jv_get_kind(msg) == JV_KIND_STRING)- return msg; // expected to already be formatted-- if (jv_get_kind(msg) == JV_KIND_INVALID)- msg = jv_invalid_get_msg(msg);-- if (jv_get_kind(msg) == JV_KIND_NULL)- return jq_format_error(msg); // ENOMEM-- // Invalid with msg; prefix with "jq: error: "-- if (jv_get_kind(msg) != JV_KIND_INVALID) {- if (jv_get_kind(msg) == JV_KIND_STRING)- return jv_string_fmt("jq: error: %s", jv_string_value(msg));-- msg = jv_dump_string(msg, JV_PRINT_INVALID);- if (jv_get_kind(msg) == JV_KIND_STRING)- return jv_string_fmt("jq: error: %s", jv_string_value(msg));- return jq_format_error(jv_null()); // ENOMEM- }-- // An invalid inside an invalid!- return jq_format_error(jv_invalid_get_msg(msg));-}--// XXX Refactor into a utility function that returns a jv and one that-// uses it and then prints that jv's string as the complete error-// message.-static void default_err_cb(void *data, jv msg) {- msg = jq_format_error(msg);- fprintf((FILE *)data, "%s\n", jv_string_value(msg));- jv_free(msg);-}--jq_state *jq_init(void) {- jq_state *jq;- jq = jv_mem_alloc_unguarded(sizeof(*jq));- if (jq == NULL)- return NULL;-- jq->bc = 0;- jq->next_label = 0;-- stack_init(&jq->stk);- jq->stk_top = 0;- jq->fork_top = 0;- jq->curr_frame = 0;- jq->error = jv_null();-- jq->err_cb = default_err_cb;- jq->err_cb_data = stderr;-- jq->attrs = jv_object();- jq->path = jv_null();- jq->value_at_path = jv_null();- return jq;-}--void jq_set_error_cb(jq_state *jq, jq_msg_cb cb, void *data) {- if (cb == NULL) {- jq->err_cb = default_err_cb;- jq->err_cb_data = stderr;- } else {- jq->err_cb = cb;- jq->err_cb_data = data;- }-}--void jq_get_error_cb(jq_state *jq, jq_msg_cb *cb, void **data) {- *cb = jq->err_cb;- *data = jq->err_cb_data;-}--void jq_set_nomem_handler(jq_state *jq, void (*nomem_handler)(void *), void *data) {- jv_nomem_handler(nomem_handler, data);- jq->nomem_handler = nomem_handler;- jq->nomem_handler_data = data;-}---void jq_start(jq_state *jq, jv input, int flags) {- jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);- jq_reset(jq);-- struct closure top = {jq->bc, -1};- struct frame* top_frame = frame_push(jq, top, 0, 0);- top_frame->retdata = 0;- top_frame->retaddr = 0;-- stack_push(jq, input);- stack_save(jq, jq->bc->code, stack_get_pos(jq));- if (flags & JQ_DEBUG_TRACE) {- jq->debug_trace_enabled = 1;- } else {- jq->debug_trace_enabled = 0;- }- jq->initial_execution = 1;-}--void jq_teardown(jq_state **jq) {- jq_state *old_jq = *jq;- if (old_jq == NULL)- return;- *jq = NULL;-- jq_reset(old_jq);- bytecode_free(old_jq->bc);- old_jq->bc = 0;- jv_free(old_jq->attrs);-- jv_mem_free(old_jq);-}--static int ret_follows(uint16_t *pc) {- if (*pc == RET)- return 1;- if (*pc++ != JUMP)- return 0;- return ret_follows(pc + *pc + 1); // FIXME, might be ironic-}--/*- * Look for tail calls that can be optimized: tail calls with no- * references left to the current frame.- *- * We're staring at this bytecode layout:- *- * CALL_JQ- * <nclosures>- * <callee closure> (2 units)- * <nclosures closures> (2 units each)- * <next instruction>- *- * A closure is:- *- * <level> (a relative frame count chased via the current frame's env)- * <index> (an index of a subfunction or closure in that frame)- *- * We're looking for:- *- * a) the next instruction is a RET or a chain of unconditional JUMPs- * that ends in a RET, and- *- * b) none of the closures -callee included- have level == 0.- */-static uint16_t tail_call_analyze(uint16_t *pc) {- assert(*pc == CALL_JQ);- pc++;- // + 1 for the callee closure- for (uint16_t nclosures = *pc++ + 1; nclosures > 0; pc++, nclosures--) {- if (*pc++ == 0)- return CALL_JQ;- }- if (ret_follows(pc))- return TAIL_CALL_JQ;- return CALL_JQ;-}--static struct bytecode *optimize_code(struct bytecode *bc) {- uint16_t *pc = bc->code;- // FIXME: Don't mutate bc->code...- while (pc < bc->code + bc->codelen) {- switch (*pc) {- case CALL_JQ:- *pc = tail_call_analyze(pc);- break;-- // Other bytecode optimizations here. A peephole optimizer would- // fit right in.- default: break;- }- pc += bytecode_operation_length(pc);- }- return bc;-}--static struct bytecode *optimize(struct bytecode *bc) {- for (int i=0; i<bc->nsubfunctions; i++) {- bc->subfunctions[i] = optimize(bc->subfunctions[i]);- }- return optimize_code(bc);-}--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;- locations = locfile_init(jq, "<top-level>", str, strlen(str));- block program;- jq_reset(jq);- if (jq->bc) {- bytecode_free(jq->bc);- jq->bc = 0;- }- int nerrors = load_program(jq, locations, &program);- if (nerrors == 0) {- jv_array_foreach(args, i, arg) {- 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, &jq->bc, locations);- }- }- if (nerrors)- jq_report_error(jq, jv_string_fmt("jq: %d compile %s", nerrors, nerrors > 1 ? "errors" : "error"));- if (jq->bc)- jq->bc = optimize(jq->bc);- jv_free(args);- locfile_free(locations);- return jq->bc != NULL;-}--int jq_compile(jq_state *jq, const char* str) {- return jq_compile_args(jq, str, jv_array());-}--jv jq_get_jq_origin(jq_state *jq) {- return jq_get_attr(jq, jv_string("JQ_ORIGIN"));-}--jv jq_get_prog_origin(jq_state *jq) {- return jq_get_attr(jq, jv_string("PROGRAM_ORIGIN"));-}--jv jq_get_lib_dirs(jq_state *jq) {- return jq_get_attr(jq, jv_string("JQ_LIBRARY_PATH"));-}--void jq_set_attrs(jq_state *jq, jv attrs) {- assert(jv_get_kind(attrs) == JV_KIND_OBJECT);- jv_free(jq->attrs);- jq->attrs = attrs;-}--void jq_set_attr(jq_state *jq, jv attr, jv val) {- jq->attrs = jv_object_set(jq->attrs, attr, val);-}--jv jq_get_attr(jq_state *jq, jv attr) {- return jv_object_get(jv_copy(jq->attrs), attr);-}--void jq_dump_disassembly(jq_state *jq, int indent) {- dump_disassembly(indent, jq->bc);-}--void jq_set_input_cb(jq_state *jq, jq_input_cb cb, void *data) {- jq->input_cb = cb;- jq->input_cb_data = data;-}--void jq_get_input_cb(jq_state *jq, jq_input_cb *cb, void **data) {- *cb = jq->input_cb;- *data = jq->input_cb_data;-}--void jq_set_debug_cb(jq_state *jq, jq_msg_cb cb, void *data) {- jq->debug_cb = cb;- jq->debug_cb_data = data;-}--void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) {- *cb = jq->debug_cb;- *data = jq->debug_cb_data;-}
inject_errors.c
@@ -1,112 +0,0 @@--#define _GNU_SOURCE /* for RTLD_NEXT */-#include <assert.h>-#include <dlfcn.h>-#include <errno.h>-#include <stdio.h>-#include <string.h>-#include <unistd.h>--static FILE *fail;-static FILE *fail_read;-static FILE *fail_write;-static FILE *fail_close;-static int error;--static FILE * (*real_fopen)(const char *, const char *);-static int (*real_fclose)(FILE *);-static int (*real_ferror)(FILE *);-static void (*real_clearerr)(FILE *);-static char * (*real_fgets)(char *, int, FILE *);-static size_t (*real_fread)(void *, size_t, size_t, FILE *);-static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *);--#define GET_REAL(sym) \- do { \- if (real_ ## sym == 0) { \- real_ ## sym = dlsym(RTLD_NEXT, #sym); \- assert(real_ ## sym != 0); \- } \- } while (0)--#define dbg_write(msg) (void)write(2, msg, sizeof(msg) - 1)--#define dbg() \- do { \- dbg_write("here: "); \- dbg_write(__func__); \- dbg_write("!\n"); \- } while (0)--FILE *fopen(const char *path, const char *mode) {- GET_REAL(fopen);- fail = fail_read = fail_write = fail_close = 0;- FILE *f = real_fopen(path, mode);- error = EIO;- if (strcmp(path, "fail_read") == 0) {- fail = fail_read = f;- } else if (strncmp(path, "fail_write", sizeof("fail_write") - 1) == 0) {- // Not that jq opens files for write anyways...- fail = fail_write = f;- if (strcmp(path, "fail_write_enospc") == 0)- error = ENOSPC;- } else if (strncmp(path, "fail_close", sizeof("fail_close") - 1) == 0) {- fail = fail_close = f;- if (strcmp(path, "fail_close_enospc") == 0)- error = ENOSPC;- }- return f;-}--int fclose(FILE *f) {- GET_REAL(fclose);- int res = real_fclose(f);- if (fail_close == f) {- fail = fail_read = fail_write = fail_close = 0;- return EOF;- }- return res;-}--char * fgets(char *buf, int len, FILE *f) {- GET_REAL(fgets);- char *res = real_fgets(buf, len, f);- if (fail_read == f)- return 0;- return res;-}--size_t fread(void *buf, size_t sz, size_t nemb, FILE *f) {- GET_REAL(fread);- size_t res = real_fread(buf, sz, nemb, f);- if (fail_read == f)- return 0;- return res;-}--size_t fwrite(const void *buf, size_t sz, size_t nemb, FILE *f) {- GET_REAL(fwrite);- size_t res = real_fwrite(buf, sz, nemb, f);- if (fail_write == f)- return 0;- return res;-}--int ferror(FILE *f) {- GET_REAL(ferror);- int res = real_ferror(f);- if (fail == f) {- errno = error;- return 1;- }- return res;-}--void clearerr(FILE *f) {- GET_REAL(clearerr);- real_clearerr(f);- if (fail == f) {- fail = fail_read = fail_write = fail_close = 0;- error = 0;- }-}
jq.h
@@ -1,60 +0,0 @@-#ifndef JQ_H-#define JQ_H--#include <stdio.h>-#include "jv.h"--enum {JQ_DEBUG_TRACE = 1};--typedef struct jq_state jq_state;-typedef void (*jq_msg_cb)(void *, jv);--jq_state *jq_init(void);-void jq_set_error_cb(jq_state *, jq_msg_cb, void *);-void jq_get_error_cb(jq_state *, jq_msg_cb *, void **);-void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);-jv jq_format_error(jv msg);-void jq_report_error(jq_state *, jv);-int jq_compile(jq_state *, const char*);-int jq_compile_args(jq_state *, const char*, jv);-void jq_dump_disassembly(jq_state *, int);-void jq_start(jq_state *, jv value, int);-jv jq_next(jq_state *);-void jq_teardown(jq_state **);--typedef jv (*jq_input_cb)(jq_state *, void *);-void jq_set_input_cb(jq_state *, jq_input_cb, void *);-void jq_get_input_cb(jq_state *, jq_input_cb *, void **);--void jq_set_debug_cb(jq_state *, jq_msg_cb, void *);-void jq_get_debug_cb(jq_state *, jq_msg_cb *, void **);--void jq_set_attrs(jq_state *, jv);-jv jq_get_attrs(jq_state *);-jv jq_get_jq_origin(jq_state *);-jv jq_get_prog_origin(jq_state *);-jv jq_get_lib_dirs(jq_state *);-void jq_set_attr(jq_state *, jv, jv);-jv jq_get_attr(jq_state *, jv);--/*- * We use char * instead of jf for filenames here because filenames- * should be in the process' locale's codeset, which may not be UTF-8,- * whereas jv string values must be in UTF-8. This way the caller- * doesn't have to perform any codeset conversions.- */-typedef struct jq_util_input_state jq_util_input_state;-typedef void (*jq_util_msg_cb)(void *, const char *);--jq_util_input_state *jq_util_input_init(jq_util_msg_cb, void *);-void jq_util_input_set_parser(jq_util_input_state *, jv_parser *, int);-void jq_util_input_free(jq_util_input_state **);-void jq_util_input_add_input(jq_util_input_state *, const char *);-int jq_util_input_errors(jq_util_input_state *);-jv jq_util_input_next_input(jq_util_input_state *);-jv jq_util_input_next_input_cb(jq_state *, void *);-jv jq_util_input_get_position(jq_state*);-jv jq_util_input_get_current_filename(jq_state*);-jv jq_util_input_get_current_line(jq_state*);--#endif /* !JQ_H */
jq_parser.h
@@ -1,9 +0,0 @@-#ifndef JQ_PARSER_H-#define JQ_PARSER_H-#include "locfile.h"-#include "compile.h"--int jq_parse(struct locfile* source, block* answer);-int jq_parse_library(struct locfile* locations, block* answer);--#endif
jq_test.c
@@ -1,346 +0,0 @@-#include <assert.h>-#include <stdio.h>-#include <string.h>-#include <stdlib.h>-#include "jv.h"-#include "jq.h"--static void jv_test();-static void run_jq_tests(jv, int, FILE *);---int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) {- FILE *testdata = stdin;- jv_test();- if (argc > 0) {- testdata = fopen(argv[0], "r");- if (!testdata) {- perror("fopen");- exit(1);- }- }- run_jq_tests(libdirs, verbose, testdata);- return 0;-}--static int skipline(const char* buf) {- int p = 0;- while (buf[p] == ' ' || buf[p] == '\t') p++;- if (buf[p] == '#' || buf[p] == '\n' || buf[p] == 0) return 1;- return 0;-}--static int checkerrormsg(const char* buf) {- return strcmp(buf, "%%FAIL\n") == 0;-}--static int checkfail(const char* buf) {- return strcmp(buf, "%%FAIL\n") == 0 || strcmp(buf, "%%FAIL IGNORE MSG\n") == 0;-}--struct err_data {- char buf[4096];-};--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);-}--static void run_jq_tests(jv lib_dirs, int verbose, 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);- 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);- must_fail = 0;- check_msg = 0;- 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++;- }- must_fail = 0;- check_msg = 0;- 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;- }- if (verbose) {- 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)) {- printf("*** Input is invalid on line %u: %s\n", lineno, buf);- invalid++;- continue;- }- jq_start(jq, input, verbose ? JQ_DEBUG_TRACE : 0);-- while (fgets(buf, sizeof(buf), testdata)) {- lineno++;- if (skipline(buf)) break;- jv expected = jv_parse(buf);- if (!jv_is_valid(expected)) {- printf("*** Expected result is invalid on line %u: %s\n", lineno, buf);- 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_COLOR|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);- }-}
jv.c
@@ -1,1333 +0,0 @@-#include <stdint.h>-#include <stddef.h>-#include <assert.h>-#include <stdlib.h>-#include <stdio.h>-#include <string.h>-#include <stdarg.h>-#include <limits.h>--#include "jv_alloc.h"-#include "jv.h"-#include "jv_unicode.h"-#include "util.h"--/*- * Internal refcounting helpers- */--typedef struct jv_refcnt {- int count;-} jv_refcnt;--static const jv_refcnt JV_REFCNT_INIT = {1};--static void jvp_refcnt_inc(jv_refcnt* c) {- c->count++;-}--static int jvp_refcnt_dec(jv_refcnt* c) {- c->count--;- return c->count == 0;-}--static int jvp_refcnt_unshared(jv_refcnt* c) {- assert(c->count > 0);- return c->count == 1;-}--/*- * Simple values (true, false, null)- */--#define KIND_MASK 0xf--jv_kind jv_get_kind(jv x) {- return x.kind_flags & KIND_MASK;-}--const char* jv_kind_name(jv_kind k) {- switch (k) {- case JV_KIND_INVALID: return "<invalid>";- case JV_KIND_NULL: return "null";- case JV_KIND_FALSE: return "boolean";- case JV_KIND_TRUE: return "boolean";- case JV_KIND_NUMBER: return "number";- case JV_KIND_STRING: return "string";- case JV_KIND_ARRAY: return "array";- case JV_KIND_OBJECT: return "object";- }- assert(0 && "invalid kind");- 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}};--jv jv_true() {- return JV_TRUE;-}--jv jv_false() {- return JV_FALSE;-}--jv jv_null() {- return JV_NULL;-}--jv jv_bool(int x) {- return x ? JV_TRUE : JV_FALSE;-}--/*- * Invalid objects, with optional error messages- */--typedef struct {- jv_refcnt refcnt;- jv errmsg;-} jvp_invalid;--jv jv_invalid_with_msg(jv err) {- if (jv_get_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}};- return x;-}--jv jv_invalid() {- return JV_INVALID;-}--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;-}--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);- 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)) {- jv_free(((jvp_invalid*)x.u.ptr)->errmsg);- jv_mem_free(x.u.ptr);- }-}--/*- * Numbers- */--jv jv_number(double x) {- jv j = {JV_KIND_NUMBER, 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;-}--int jv_is_integer(jv j){- if(jv_get_kind(j) != JV_KIND_NUMBER){- return 0;- }- double x = jv_number_value(j);- if(x != x || x > INT_MAX || x < INT_MIN){- return 0;- }-- return x == (int)x;-}--/*- * Arrays (internal helpers)- */--#define ARRAY_SIZE_ROUND_UP(n) (((n)*3)/2)--static int imax(int a, int b) {- if (a>b) return a;- else return b;-}--//FIXME signed vs unsigned-typedef struct {- jv_refcnt refcnt;- int length, alloc_length;- jv elements[];-} jvp_array;--static jvp_array* jvp_array_ptr(jv a) {- assert(jv_get_kind(a) == JV_KIND_ARRAY);- return (jvp_array*)a.u.ptr;-}--static jvp_array* jvp_array_alloc(unsigned size) {- jvp_array* a = jv_mem_alloc(sizeof(jvp_array) + sizeof(jv) * size);- a->refcnt.count = 1;- a->length = 0;- a->alloc_length = size;- return a;-}--static jv jvp_array_new(unsigned size) {- jv r = {JV_KIND_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);- if (jvp_refcnt_dec(a.u.ptr)) {- jvp_array* array = jvp_array_ptr(a);- for (int i=0; i<array->length; i++) {- jv_free(array->elements[i]);- }- jv_mem_free(array);- }-}--static int jvp_array_length(jv a) {- assert(jv_get_kind(a) == JV_KIND_ARRAY);- return a.size;-}--static int jvp_array_offset(jv a) {- assert(jv_get_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);- if (i >= 0 && i < jvp_array_length(a)) {- jvp_array* array = jvp_array_ptr(a);- assert(i + jvp_array_offset(a) < array->length);- return &array->elements[i + jvp_array_offset(a)];- } else {- return 0;- }-}--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];- }-}--static int jvp_array_equal(jv a, jv b) {- if (jvp_array_length(a) != jvp_array_length(b))- return 0;- if (jvp_array_ptr(a) == jvp_array_ptr(b) &&- jvp_array_offset(a) == jvp_array_offset(b))- return 1;- for (int i=0; i<jvp_array_length(a); i++) {- if (!jv_equal(jv_copy(*jvp_array_read(a, i)),- jv_copy(*jvp_array_read(b, i))))- return 0;- }- return 1;-}--static void jvp_clamp_slice_params(int len, int *pstart, int *pend)-{- if (*pstart < 0) *pstart = len + *pstart;- if (*pend < 0) *pend = len + *pend;-- if (*pstart < 0) *pstart = 0;- if (*pstart > len) *pstart = len;- if (*pend > len) *pend = len;- if (*pend < *pstart) *pend = *pstart;-}--static jv jvp_array_slice(jv a, int start, int end) {- assert(jv_get_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);-- // FIXME: maybe slice should reallocate if the slice is small enough- if (start == end) {- jv_free(a);- return jv_array();- }- // FIXME FIXME FIXME large offsets- a.offset += start;- a.size = end - start;- return a;-}--/*- * Arrays (public interface)- */--jv jv_array_sized(int n) {- return jvp_array_new(n);-}--jv jv_array() {- return jv_array_sized(16);-}--int jv_array_length(jv j) {- assert(jv_get_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);- jv* slot = jvp_array_read(j, idx);- jv val;- if (slot) {- val = jv_copy(*slot);- } else {- val = jv_invalid();- }- jv_free(j);- return val;-}--jv jv_array_set(jv j, int idx, jv val) {- assert(jv_get_kind(j) == JV_KIND_ARRAY);-- if (idx < 0)- idx = jvp_array_length(j) + idx;- if (idx < 0) {- jv_free(j);- jv_free(val);- return jv_invalid_with_msg(jv_string("Out of bounds negative array index"));- }- // copy/free of val,j coalesced- jv* slot = jvp_array_write(&j, idx);- jv_free(*slot);- *slot = val;- return j;-}--jv jv_array_append(jv j, jv val) {- // copy/free of val,j coalesced- return jv_array_set(j, jv_array_length(jv_copy(j)), 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);-- // FIXME: could be faster- jv_array_foreach(b, i, elem) {- a = jv_array_append(a, elem);- }- jv_free(b);- return a;-}--jv jv_array_slice(jv a, int start, int end) {- assert(jv_get_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_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))))- idx = -1;- else if (bi == 0 && idx == -1)- idx = ai;- }- if (idx > -1)- res = jv_array_append(res, jv_number(idx));- idx = -1;- }- jv_free(a);- jv_free(b);- return res;-}---/*- * Strings (internal helpers)- */--typedef struct {- jv_refcnt refcnt;- uint32_t hash;- // high 31 bits are length, low bit is a flag- // indicating whether hash has been computed.- uint32_t length_hashed;- uint32_t alloc_length;- char data[];-} jvp_string;--static jvp_string* jvp_string_ptr(jv a) {- assert(jv_get_kind(a) == JV_KIND_STRING);- return (jvp_string*)a.u.ptr;-}--static jvp_string* jvp_string_alloc(uint32_t size) {- jvp_string* s = jv_mem_alloc(sizeof(jvp_string) + size + 1);- s->refcnt.count = 1;- s->alloc_length = size;- return s;-}--/* Copy a UTF8 string, replacing all badly encoded points with U+FFFD */-static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {- const char* end = data + length;- const char* i = data;- const char* cstart;-- uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD- jvp_string* s = jvp_string_alloc(maxlength);- char* out = s->data;- int c = 0;-- while ((i = jvp_utf8_next((cstart = i), end, &c))) {- if (c == -1) {- c = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER- }- out += jvp_utf8_encode(c, out);- assert(out < s->data + maxlength);- }- length = out - s->data;- s->data[length] = 0;- s->length_hashed = length << 1;- jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}};- return r;-}--/* Assumes valid UTF8 */-static jv jvp_string_new(const char* data, uint32_t length) {- jvp_string* s = jvp_string_alloc(length);- s->length_hashed = length << 1;- if (data != NULL)- memcpy(s->data, data, length);- s->data[length] = 0;- jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}};- return r;-}--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}};- return r;-}---static void jvp_string_free(jv js) {- jvp_string* s = jvp_string_ptr(js);- if (jvp_refcnt_dec(&s->refcnt)) {- jv_mem_free(s);- }-}--static uint32_t jvp_string_length(jvp_string* s) {- return s->length_hashed >> 1;-}--static uint32_t jvp_string_remaining_space(jvp_string* s) {- assert(s->alloc_length >= jvp_string_length(s));- uint32_t r = s->alloc_length - jvp_string_length(s);- return r;-}--static jv jvp_string_append(jv string, const char* data, uint32_t len) {- jvp_string* s = jvp_string_ptr(string);- uint32_t currlen = jvp_string_length(s);-- if (jvp_refcnt_unshared(string.u.ptr) &&- jvp_string_remaining_space(s) >= len) {- // the next string fits at the end of a- memcpy(s->data + currlen, data, len);- s->data[currlen + len] = 0;- s->length_hashed = (currlen + len) << 1;- return string;- } else {- // allocate a bigger buffer and copy- uint32_t allocsz = (currlen + len) * 2;- if (allocsz < 32) allocsz = 32;- jvp_string* news = jvp_string_alloc(allocsz);- news->length_hashed = (currlen + len) << 1;- memcpy(news->data, s->data, currlen);- 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}};- return r;- }-}--static const uint32_t HASH_SEED = 0x432A9843;--static uint32_t rotl32 (uint32_t x, int8_t r){- return (x << r) | (x >> (32 - r));-}--static uint32_t jvp_string_hash(jv jstr) {- jvp_string* str = jvp_string_ptr(jstr);- if (str->length_hashed & 1)- return str->hash;-- /* The following is based on MurmurHash3.- MurmurHash3 was written by Austin Appleby, and is placed- in the public domain. */-- const uint8_t* data = (const uint8_t*)str->data;- int len = (int)jvp_string_length(str);- const int nblocks = len / 4;-- uint32_t h1 = HASH_SEED;-- const uint32_t c1 = 0xcc9e2d51;- const uint32_t c2 = 0x1b873593;- const uint32_t* blocks = (const uint32_t *)(data + nblocks*4);-- for(int i = -nblocks; i; i++) {- uint32_t k1 = blocks[i]; //FIXME: endianness/alignment-- k1 *= c1;- k1 = rotl32(k1,15);- k1 *= c2;-- h1 ^= k1;- h1 = rotl32(h1,13);- h1 = h1*5+0xe6546b64;- }-- const uint8_t* tail = (const uint8_t*)(data + nblocks*4);-- uint32_t k1 = 0;-- switch(len & 3) {- case 3: k1 ^= tail[2] << 16;- case 2: k1 ^= tail[1] << 8;- case 1: k1 ^= tail[0];- k1 *= c1; k1 = rotl32(k1,15); k1 *= c2; h1 ^= k1;- }-- h1 ^= len;-- h1 ^= h1 >> 16;- h1 *= 0x85ebca6b;- h1 ^= h1 >> 13;- h1 *= 0xc2b2ae35;- h1 ^= h1 >> 16;-- str->length_hashed |= 1;- str->hash = h1;-- 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);- 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;- return memcmp(stra->data, strb->data, jvp_string_length(stra)) == 0;-}--/*- * Strings (public API)- */--jv jv_string_sized(const char* str, int len) {- return- jvp_utf8_is_valid(str, str+len) ?- jvp_string_new(str, len) :- jvp_string_copy_replace_bad(str, len);-}--jv jv_string_empty(int len) {- return jvp_string_empty_new(len);-}--jv jv_string(const char* str) {- return jv_string_sized(str, strlen(str));-}--int jv_string_length_bytes(jv j) {- assert(jv_get_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);- const char* i = jv_string_value(j);- const char* end = i + jv_string_length_bytes(jv_copy(j));- int c = 0, len = 0;- while ((i = jvp_utf8_next(i, end, &c))) len++;- jv_free(j);- return len;-}---jv jv_string_indexes(jv j, jv k) {- assert(jv_get_kind(j) == JV_KIND_STRING);- assert(jv_get_kind(k) == JV_KIND_STRING);- const char *jstr = jv_string_value(j);- const char *idxstr = jv_string_value(k);- const char *p;- int jlen = jv_string_length_bytes(jv_copy(j));- int idxlen = jv_string_length_bytes(jv_copy(k));- jv a = jv_array();-- p = jstr;- while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) {- a = jv_array_append(a, jv_number(p - jstr));- p += idxlen;- }- jv_free(j);- jv_free(k);- return a;-}--jv jv_string_split(jv j, jv sep) {- assert(jv_get_kind(j) == JV_KIND_STRING);- assert(jv_get_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);- const char *p, *s;- int seplen = jv_string_length_bytes(jv_copy(sep));- jv a = jv_array();-- assert(jv_get_refcnt(a) == 1);-- if (seplen == 0) {- int c;- while ((jstr = jvp_utf8_next(jstr, jend, &c)))- a = jv_array_append(a, jv_string_append_codepoint(jv_string(""), c));- } else {- for (p = jstr; p < jend; p = s + seplen) {- s = _jq_memmem(p, jend - p, sepstr, seplen);- if (s == NULL)- s = jend;- a = jv_array_append(a, jv_string_sized(p, s - p));- // Add an empty string to denote that j ends on a sep- if (s + seplen == jend && seplen != 0)- a = jv_array_append(a, jv_string(""));- }- }- jv_free(j);- jv_free(sep);- return a;-}--jv jv_string_explode(jv j) {- assert(jv_get_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;- jv a = jv_array_sized(len);- int c;- while ((i = jvp_utf8_next(i, end, &c)))- a = jv_array_append(a, jv_number(c));- jv_free(j);- return a;-}--jv jv_string_implode(jv j) {- assert(jv_get_kind(j) == JV_KIND_ARRAY);- int len = jv_array_length(jv_copy(j));- jv s = jv_string_empty(len);- int i;-- assert(len >= 0);-- for (i = 0; i < len; i++) {- jv n = jv_array_get(jv_copy(j), i);- assert(jv_get_kind(n) == JV_KIND_NUMBER);- int nv = jv_number_value(n);- if (nv > 0x10FFFF)- nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER- s = jv_string_append_codepoint(s, nv);- }-- jv_free(j);- return s;-}--unsigned long jv_string_hash(jv j) {- assert(jv_get_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);- return jvp_string_ptr(j)->data;-}--jv jv_string_slice(jv j, int start, int end) {- assert(jv_get_kind(j) == JV_KIND_STRING);- const char *s = jv_string_value(j);- int len = jv_string_length_bytes(jv_copy(j));- int i;- const char *p, *e;- int c;- jv res;-- jvp_clamp_slice_params(len, &start, &end);- assert(0 <= start && start <= end && end <= len);-- /* Look for byte offset corresponding to start codepoints */- for (p = s, i = 0; i < start; i++) {- p = jvp_utf8_next(p, s + len, &c);- if (p == NULL) {- jv_free(j);- return jv_string_empty(16);- }- if (c == -1) {- jv_free(j);- return jv_invalid_with_msg(jv_string("Invalid UTF-8 string"));- }- }- /* Look for byte offset corresponding to end codepoints */- for (e = p; e != NULL && i < end; i++) {- e = jvp_utf8_next(e, s + len, &c);- if (e == NULL) {- e = s + len;- break;- }- if (c == -1) {- jv_free(j);- return jv_invalid_with_msg(jv_string("Invalid UTF-8 string"));- }- }-- /*- * NOTE: Ideally we should do here what jvp_array_slice() does instead- * of allocating a new string as we do! However, we assume NUL-- * terminated strings all over, and in the jv API, so for now we waste- * memory like a drunken navy programmer. There's probably nothing we- * can do about it.- */- res = jv_string_sized(p, e - p);- jv_free(j);- return res;-}--jv jv_string_concat(jv a, jv b) {- a = jvp_string_append(a, jv_string_value(b),- jvp_string_length(jvp_string_ptr(b)));- jv_free(b);- return a;-}--jv jv_string_append_buf(jv a, const char* buf, int len) {- if (jvp_utf8_is_valid(buf, buf+len)) {- a = jvp_string_append(a, buf, len);- } else {- jv b = jvp_string_copy_replace_bad(buf, len);- a = jv_string_concat(a, b);- }- return a;-}--jv jv_string_append_codepoint(jv a, uint32_t c) {- char buf[5];- int len = jvp_utf8_encode(c, buf);- a = jvp_string_append(a, buf, len);- return a;-}--jv jv_string_append_str(jv a, const char* str) {- return jv_string_append_buf(a, str, strlen(str));-}--jv jv_string_vfmt(const char* fmt, va_list ap) {- int size = 1024;- while (1) {- char* buf = jv_mem_alloc(size);- va_list ap2;- va_copy(ap2, ap);- int n = vsnprintf(buf, size, fmt, ap2);- va_end(ap2);- /*- * NOTE: here we support old vsnprintf()s that return -1 because the- * buffer is too small.- */- if (n >= 0 && n < size) {- jv ret = jv_string_sized(buf, n);- jv_mem_free(buf);- return ret;- } else {- jv_mem_free(buf);- size = (n > 0) ? /* standard */ (n * 2) : /* not standard */ (size * 2);- }- }-}--jv jv_string_fmt(const char* fmt, ...) {- va_list args;- va_start(args, fmt);- jv res = jv_string_vfmt(fmt, args);- va_end(args);- return res;-}--/*- * Objects (internal helpers)- */--struct object_slot {- int next; /* next slot with same hash, for collisions */- uint32_t hash;- jv string;- jv value;-};--typedef struct {- jv_refcnt refcnt;- int next_free;- struct object_slot elements[];-} jvp_object;---/* warning: nontrivial justification of alignment */-static jv jvp_object_new(int size) {- // Allocates an object of (size) slots and (size*2) hash buckets.-- // size must be a power of two- assert(size > 0 && (size & (size - 1)) == 0);-- jvp_object* obj = jv_mem_alloc(sizeof(jvp_object) +- sizeof(struct object_slot) * size +- sizeof(int) * (size * 2));- obj->refcnt.count = 1;- for (int i=0; i<size; i++) {- obj->elements[i].next = i - 1;- obj->elements[i].string = JV_NULL;- obj->elements[i].hash = 0;- obj->elements[i].value = JV_NULL;- }- obj->next_free = 0;- int* hashbuckets = (int*)(&obj->elements[size]);- for (int i=0; i<size*2; i++) {- hashbuckets[i] = -1;- }- jv r = {JV_KIND_OBJECT, 0, 0, size, {&obj->refcnt}};- return r;-}--static jvp_object* jvp_object_ptr(jv o) {- assert(jv_get_kind(o) == JV_KIND_OBJECT);- return (jvp_object*)o.u.ptr;-}--static uint32_t jvp_object_mask(jv o) {- assert(jv_get_kind(o) == JV_KIND_OBJECT);- return (o.size * 2) - 1;-}--static int jvp_object_size(jv o) {- assert(jv_get_kind(o) == JV_KIND_OBJECT);- return o.size;-}--static int* jvp_object_buckets(jv o) {- return (int*)(&jvp_object_ptr(o)->elements[o.size]);-}--static int* jvp_object_find_bucket(jv object, jv key) {- return jvp_object_buckets(object) + (jvp_object_mask(object) & jvp_string_hash(key));-}--static struct object_slot* jvp_object_get_slot(jv object, int slot) {- assert(slot == -1 || (slot >= 0 && slot < jvp_object_size(object)));- if (slot == -1) return 0;- else return &jvp_object_ptr(object)->elements[slot];-}--static struct object_slot* jvp_object_next_slot(jv object, struct object_slot* slot) {- return jvp_object_get_slot(object, slot->next);-}--static struct object_slot* jvp_object_find_slot(jv object, jv keystr, int* bucket) {- uint32_t hash = jvp_string_hash(keystr);- for (struct object_slot* curr = jvp_object_get_slot(object, *bucket);- curr;- curr = jvp_object_next_slot(object, curr)) {- if (curr->hash == hash && jvp_string_equal(keystr, curr->string)) {- return curr;- }- }- return 0;-}--static struct object_slot* jvp_object_add_slot(jv object, jv key, int* bucket) {- jvp_object* o = jvp_object_ptr(object);- int newslot_idx = o->next_free;- if (newslot_idx == jvp_object_size(object)) return 0;- struct object_slot* newslot = jvp_object_get_slot(object, newslot_idx);- o->next_free++;- newslot->next = *bucket;- *bucket = newslot_idx;- newslot->hash = jvp_string_hash(key);- newslot->string = key;- return newslot;-}--static jv* jvp_object_read(jv object, jv key) {- assert(jv_get_kind(key) == JV_KIND_STRING);- int* bucket = jvp_object_find_bucket(object, key);- struct object_slot* slot = jvp_object_find_slot(object, key, bucket);- if (slot == 0) return 0;- else return &slot->value;-}--static void jvp_object_free(jv o) {- assert(jv_get_kind(o) == JV_KIND_OBJECT);- if (jvp_refcnt_dec(o.u.ptr)) {- for (int i=0; i<jvp_object_size(o); i++) {- struct object_slot* slot = jvp_object_get_slot(o, i);- if (jv_get_kind(slot->string) != JV_KIND_NULL) {- jvp_string_free(slot->string);- jv_free(slot->value);- }- }- jv_mem_free(jvp_object_ptr(o));- }-}--static jv jvp_object_rehash(jv object) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- assert(jvp_refcnt_unshared(object.u.ptr));- int size = jvp_object_size(object);- jv new_object = jvp_object_new(size * 2);- for (int i=0; i<size; i++) {- struct object_slot* slot = jvp_object_get_slot(object, i);- if (jv_get_kind(slot->string) == JV_KIND_NULL) continue;- int* new_bucket = jvp_object_find_bucket(new_object, slot->string);- assert(!jvp_object_find_slot(new_object, slot->string, new_bucket));- struct object_slot* new_slot = jvp_object_add_slot(new_object, slot->string, new_bucket);- assert(new_slot);- new_slot->value = slot->value;- }- // references are transported, just drop the old table- jv_mem_free(jvp_object_ptr(object));- return new_object;-}--static jv jvp_object_unshare(jv object) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- if (jvp_refcnt_unshared(object.u.ptr))- return object;-- jv new_object = jvp_object_new(jvp_object_size(object));- jvp_object_ptr(new_object)->next_free = jvp_object_ptr(object)->next_free;- for (int i=0; i<jvp_object_size(new_object); i++) {- struct object_slot* old_slot = jvp_object_get_slot(object, i);- struct object_slot* new_slot = jvp_object_get_slot(new_object, i);- *new_slot = *old_slot;- if (jv_get_kind(old_slot->string) != JV_KIND_NULL) {- new_slot->string = jv_copy(old_slot->string);- new_slot->value = jv_copy(old_slot->value);- }- }-- int* old_buckets = jvp_object_buckets(object);- int* new_buckets = jvp_object_buckets(new_object);- memcpy(new_buckets, old_buckets, sizeof(int) * jvp_object_size(new_object)*2);-- jvp_object_free(object);- assert(jvp_refcnt_unshared(new_object.u.ptr));- return new_object;-}--static jv* jvp_object_write(jv* object, jv key) {- *object = jvp_object_unshare(*object);- int* bucket = jvp_object_find_bucket(*object, key);- struct object_slot* slot = jvp_object_find_slot(*object, key, bucket);- if (slot) {- // already has the key- jvp_string_free(key);- return &slot->value;- }- slot = jvp_object_add_slot(*object, key, bucket);- if (slot) {- slot->value = jv_invalid();- } else {- *object = jvp_object_rehash(*object);- bucket = jvp_object_find_bucket(*object, key);- assert(!jvp_object_find_slot(*object, key, bucket));- slot = jvp_object_add_slot(*object, key, bucket);- assert(slot);- slot->value = jv_invalid();- }- return &slot->value;-}--static int jvp_object_delete(jv* object, jv key) {- assert(jv_get_kind(key) == JV_KIND_STRING);- *object = jvp_object_unshare(*object);- int* bucket = jvp_object_find_bucket(*object, key);- int* prev_ptr = bucket;- uint32_t hash = jvp_string_hash(key);- for (struct object_slot* curr = jvp_object_get_slot(*object, *bucket);- curr;- curr = jvp_object_next_slot(*object, curr)) {- if (hash == curr->hash && jvp_string_equal(key, curr->string)) {- *prev_ptr = curr->next;- jvp_string_free(curr->string);- curr->string = JV_NULL;- jv_free(curr->value);- return 1;- }- prev_ptr = &curr->next;- }- return 0;-}--static int jvp_object_length(jv object) {- int n = 0;- for (int i=0; i<jvp_object_size(object); i++) {- struct object_slot* slot = jvp_object_get_slot(object, i);- if (jv_get_kind(slot->string) != JV_KIND_NULL) n++;- }- return n;-}--static int jvp_object_equal(jv o1, jv o2) {- int len2 = jvp_object_length(o2);- int len1 = 0;- for (int i=0; i<jvp_object_size(o1); i++) {- struct object_slot* slot = jvp_object_get_slot(o1, i);- if (jv_get_kind(slot->string) == JV_KIND_NULL) continue;- jv* slot2 = jvp_object_read(o2, slot->string);- if (!slot2) return 0;- // FIXME: do less refcounting here- if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0;- len1++;- }- return len1 == len2;-}--/*- * Objects (public interface)- */-#define DEFAULT_OBJECT_SIZE 8-jv jv_object() {- return jvp_object_new(8);-}--jv jv_object_get(jv object, jv key) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- assert(jv_get_kind(key) == JV_KIND_STRING);- jv* slot = jvp_object_read(object, key);- jv val;- if (slot) {- val = jv_copy(*slot);- } else {- val = jv_invalid();- }- jv_free(object);- jv_free(key);- return val;-}--jv jv_object_set(jv object, jv key, jv value) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- assert(jv_get_kind(key) == JV_KIND_STRING);- // copy/free of object, key, value coalesced- jv* slot = jvp_object_write(&object, key);- jv_free(*slot);- *slot = value;- return object;-}--jv jv_object_delete(jv object, jv key) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- assert(jv_get_kind(key) == JV_KIND_STRING);- jvp_object_delete(&object, key);- jv_free(key);- return object;-}--int jv_object_length(jv object) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- int n = jvp_object_length(object);- jv_free(object);- return n;-}--jv jv_object_merge(jv a, jv b) {- assert(jv_get_kind(a) == JV_KIND_OBJECT);- jv_object_foreach(b, k, v) {- a = jv_object_set(a, k, v);- }- jv_free(b);- return a;-}--jv jv_object_merge_recursive(jv a, jv b) {- assert(jv_get_kind(a) == JV_KIND_OBJECT);- assert(jv_get_kind(b) == JV_KIND_OBJECT);-- jv_object_foreach(b, k, v) {- jv elem = jv_object_get(jv_copy(a), jv_copy(k));- if (jv_is_valid(elem) &&- jv_get_kind(elem) == JV_KIND_OBJECT &&- jv_get_kind(v) == JV_KIND_OBJECT) {- a = jv_object_set(a, k, jv_object_merge_recursive(elem, v));- } else {- jv_free(elem);- a = jv_object_set(a, k, v);- }- }- jv_free(b);- return a;-}--int jv_object_contains(jv a, jv b) {- assert(jv_get_kind(a) == JV_KIND_OBJECT);- assert(jv_get_kind(b) == JV_KIND_OBJECT);- int r = 1;-- jv_object_foreach(b, key, b_val) {- jv a_val = jv_object_get(jv_copy(a), jv_copy(key));-- r = jv_contains(a_val, b_val);- jv_free(key);-- if (!r) break;- }-- jv_free(a);- jv_free(b);- return r;-}--/*- * Object iteration (internal helpers)- */--enum { ITER_FINISHED = -2 };--int jv_object_iter_valid(jv object, int i) {- return i != ITER_FINISHED;-}--int jv_object_iter(jv object) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- return jv_object_iter_next(object, -1);-}--int jv_object_iter_next(jv object, int iter) {- assert(jv_get_kind(object) == JV_KIND_OBJECT);- assert(iter != ITER_FINISHED);- struct object_slot* slot;- do {- iter++;- if (iter >= jvp_object_size(object))- return ITER_FINISHED;- slot = jvp_object_get_slot(object, iter);- } while (jv_get_kind(slot->string) == JV_KIND_NULL);- assert(jv_get_kind(jvp_object_get_slot(object,iter)->string)- == JV_KIND_STRING);- return iter;-}--jv jv_object_iter_key(jv object, int iter) {- jv s = jvp_object_get_slot(object, iter)->string;- assert(jv_get_kind(s) == JV_KIND_STRING);- return jv_copy(s);-}--jv jv_object_iter_value(jv object, int iter) {- return jv_copy(jvp_object_get_slot(object, iter)->value);-}--/*- * Memory management- */-jv jv_copy(jv j) {- if (jv_get_kind(j) == JV_KIND_ARRAY ||- jv_get_kind(j) == JV_KIND_STRING ||- jv_get_kind(j) == JV_KIND_OBJECT ||- (jv_get_kind(j) == JV_KIND_INVALID && j.u.ptr != 0)) {- jvp_refcnt_inc(j.u.ptr);- }- return j;-}--void jv_free(jv j) {- if (jv_get_kind(j) == JV_KIND_ARRAY) {- jvp_array_free(j);- } else if (jv_get_kind(j) == JV_KIND_STRING) {- jvp_string_free(j);- } else if (jv_get_kind(j) == JV_KIND_OBJECT) {- jvp_object_free(j);- } else if (jv_get_kind(j) == JV_KIND_INVALID) {- jvp_invalid_free(j);- }-}--int jv_get_refcnt(jv j) {- switch (jv_get_kind(j)) {- case JV_KIND_ARRAY:- case JV_KIND_STRING:- case JV_KIND_OBJECT:- return j.u.ptr->count;- default:- return 1;- }-}--/*- * Higher-level operations- */--int jv_equal(jv a, jv b) {- int r;- if (jv_get_kind(a) != jv_get_kind(b)) {- r = 0;- } else if (jv_get_kind(a) == JV_KIND_NUMBER) {- r = jv_number_value(a) == jv_number_value(b);- } else if (a.kind_flags == b.kind_flags &&- a.size == b.size &&- a.u.ptr == b.u.ptr) {- r = 1;- } else {- switch (jv_get_kind(a)) {- case JV_KIND_ARRAY:- r = jvp_array_equal(a, b);- break;- case JV_KIND_STRING:- r = jvp_string_equal(a, b);- break;- case JV_KIND_OBJECT:- r = jvp_object_equal(a, b);- break;- default:- r = 1;- break;- }- }- jv_free(a);- jv_free(b);- return r;-}--int jv_identical(jv a, jv b) {- int r;- if (a.kind_flags != b.kind_flags- || a.offset != b.offset- || a.size != b.size) {- r = 0;- } else {- switch (jv_get_kind(a)) {- case JV_KIND_ARRAY:- case JV_KIND_STRING:- case JV_KIND_OBJECT:- r = a.u.ptr == b.u.ptr;- break;- case JV_KIND_NUMBER:- r = a.u.number == b.u.number;- break;- default:- r = 1;- break;- }- }- jv_free(a);- jv_free(b);- return r;-}--int jv_contains(jv a, jv b) {- int r = 1;- if (jv_get_kind(a) != jv_get_kind(b)) {- r = 0;- } else if (jv_get_kind(a) == JV_KIND_OBJECT) {- r = jv_object_contains(jv_copy(a), jv_copy(b));- } else if (jv_get_kind(a) == JV_KIND_ARRAY) {- r = jv_array_contains(jv_copy(a), jv_copy(b));- } else if (jv_get_kind(a) == JV_KIND_STRING) {- r = strstr(jv_string_value(a), jv_string_value(b)) != 0;- } else {- r = jv_equal(jv_copy(a), jv_copy(b));- }- jv_free(a);- jv_free(b);- return r;-}
jv.h
@@ -1,240 +0,0 @@-#ifndef JV_H-#define JV_H--#include <stdarg.h>-#include <stdint.h>-#include <stdio.h>--typedef enum {- JV_KIND_INVALID,- JV_KIND_NULL,- JV_KIND_FALSE,- JV_KIND_TRUE,- JV_KIND_NUMBER,- JV_KIND_STRING,- JV_KIND_ARRAY,- JV_KIND_OBJECT-} jv_kind;--struct jv_refcnt;--/* All of the fields of this struct are private.- Really. Do not play with them. */-typedef struct {- unsigned char kind_flags;- unsigned char pad_;- unsigned short offset; /* array offsets */- int size;- union {- struct jv_refcnt* ptr;- double number;- } u;-} jv;--/*- * All jv_* functions consume (decref) input and produce (incref) output- * Except jv_copy- */--jv_kind jv_get_kind(jv);-const char* jv_kind_name(jv_kind);-static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; }--jv jv_copy(jv);-void jv_free(jv);--int jv_get_refcnt(jv);--int jv_equal(jv, jv);-int jv_identical(jv, jv);-int jv_contains(jv, jv);--jv jv_invalid(void);-jv jv_invalid_with_msg(jv);-jv jv_invalid_get_msg(jv);-int jv_invalid_has_msg(jv);---jv jv_null(void);-jv jv_true(void);-jv jv_false(void);-jv jv_bool(int);--jv jv_number(double);-double jv_number_value(jv);-int jv_is_integer(jv);--jv jv_array(void);-jv jv_array_sized(int);-int jv_array_length(jv);-jv jv_array_get(jv, int);-jv jv_array_set(jv, int, jv);-jv jv_array_append(jv, jv);-jv jv_array_concat(jv, jv);-jv jv_array_slice(jv, int, int);-jv jv_array_indexes(jv, jv);-#define jv_array_foreach(a, i, x) \- for (int jv_len__ = jv_array_length(jv_copy(a)), i=0, jv_j__ = 1; \- jv_j__; jv_j__ = 0) \- for (jv x; \- i < jv_len__ ? \- (x = jv_array_get(jv_copy(a), i), 1) : 0; \- i++)--#define JV_ARRAY_1(e) (jv_array_append(jv_array(),e))-#define JV_ARRAY_2(e1,e2) (jv_array_append(JV_ARRAY_1(e1),e2))-#define JV_ARRAY_3(e1,e2,e3) (jv_array_append(JV_ARRAY_2(e1,e2),e3))-#define JV_ARRAY_4(e1,e2,e3,e4) (jv_array_append(JV_ARRAY_3(e1,e2,e3),e4))-#define JV_ARRAY_5(e1,e2,e3,e4,e5) (jv_array_append(JV_ARRAY_4(e1,e2,e3,e4),e5))-#define JV_ARRAY_6(e1,e2,e3,e4,e5,e6) (jv_array_append(JV_ARRAY_5(e1,e2,e3,e4,e5),e6))-#define JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7) (jv_array_append(JV_ARRAY_6(e1,e2,e3,e4,e5,e6),e7))-#define JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8) (jv_array_append(JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7),e8))-#define JV_ARRAY_9(e1,e2,e3,e4,e5,e6,e7,e8,e9) (jv_array_append(JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8),e9))-#define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME-#define JV_ARRAY(...) \- JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_9, JV_ARRAY_8, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1, dummy)(__VA_ARGS__)--#ifdef __GNUC__-#define JV_PRINTF_LIKE(fmt_arg_num, args_num) \- __attribute__ ((__format__( __printf__, fmt_arg_num, args_num)))-#define JV_VPRINTF_LIKE(fmt_arg_num) \- __attribute__ ((__format__( __printf__, fmt_arg_num, 0)))-#endif---jv jv_string(const char*);-jv jv_string_sized(const char*, int);-jv jv_string_empty(int len);-int jv_string_length_bytes(jv);-int jv_string_length_codepoints(jv);-unsigned long jv_string_hash(jv);-const char* jv_string_value(jv);-jv jv_string_indexes(jv j, jv k);-jv jv_string_slice(jv j, int start, int end);-jv jv_string_concat(jv, jv);-jv jv_string_vfmt(const char*, va_list) JV_VPRINTF_LIKE(1);-jv jv_string_fmt(const char*, ...) JV_PRINTF_LIKE(1, 2);-jv jv_string_append_codepoint(jv a, uint32_t c);-jv jv_string_append_buf(jv a, const char* buf, int len);-jv jv_string_append_str(jv a, const char* str);-jv jv_string_split(jv j, jv sep);-jv jv_string_explode(jv j);-jv jv_string_implode(jv j);--jv jv_object(void);-jv jv_object_get(jv object, jv key);-jv jv_object_set(jv object, jv key, jv value);-jv jv_object_delete(jv object, jv key);-int jv_object_length(jv object);-jv jv_object_merge(jv, jv);-jv jv_object_merge_recursive(jv, jv);--int jv_object_iter(jv);-int jv_object_iter_next(jv, int);-int jv_object_iter_valid(jv, int);-jv jv_object_iter_key(jv, int);-jv jv_object_iter_value(jv, int);-#define jv_object_foreach(t, k, v) \- for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \- for (jv k, v; \- jv_object_iter_valid((t), jv_i__) ? \- (k = jv_object_iter_key(t, jv_i__), \- v = jv_object_iter_value(t, jv_i__), \- 1) \- : 0; \- jv_i__ = jv_object_iter_next(t, jv_i__)) \--#define JV_OBJECT_1(k) (jv_object_set(jv_object(),(k),jv_null()))-#define JV_OBJECT_2(k1,v1) (jv_object_set(jv_object(),(k1),(v1)))-#define JV_OBJECT_3(k1,v1,k2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,jv_null()))-#define JV_OBJECT_4(k1,v1,k2,v2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,v2))-#define JV_OBJECT_5(k1,v1,k2,v2,k3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,jv_null))-#define JV_OBJECT_6(k1,v1,k2,v2,k3,v3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,v3))-#define JV_OBJECT_7(k1,v1,k2,v2,k3,v3,k4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,jv_null()))-#define JV_OBJECT_8(k1,v1,k2,v2,k3,v3,k4,v4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,v4))-#define JV_OBJECT_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME-#define JV_OBJECT(...) \- JV_OBJECT_IDX(__VA_ARGS__, JV_OBJECT_8, JV_OBJECT_7, JV_OBJECT_6, JV_OBJECT_5, JV_OBJECT_4, JV_OBJECT_3, JV_OBJECT_2, JV_OBJECT_1)(__VA_ARGS__)----int jv_get_refcnt(jv);--enum jv_print_flags {- JV_PRINT_PRETTY = 1,- JV_PRINT_ASCII = 2,- JV_PRINT_COLOR = 4,- JV_PRINT_SORTED = 8,- JV_PRINT_INVALID = 16,- JV_PRINT_REFCOUNT = 32,- JV_PRINT_TAB = 64,- JV_PRINT_ISATTY = 128,- JV_PRINT_SPACE0 = 256,- JV_PRINT_SPACE1 = 512,- JV_PRINT_SPACE2 = 1024,-};-#define JV_PRINT_INDENT_FLAGS(n) \- ((n) < 0 || (n) > 7 ? JV_PRINT_TAB | JV_PRINT_PRETTY : (n) == 0 ? 0 : (n) << 8 | JV_PRINT_PRETTY)-void jv_dumpf(jv, FILE *f, int flags);-void jv_dump(jv, int flags);-void jv_show(jv, int flags);-jv jv_dump_string(jv, int flags);-char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize);--enum {- JV_PARSE_SEQ = 1,- JV_PARSE_STREAMING = 2,- JV_PARSE_STREAM_ERRORS = 4,-};--jv jv_parse(const char* string);-jv jv_parse_sized(const char* string, int length);--typedef void (*jv_nomem_handler_f)(void *);-void jv_nomem_handler(jv_nomem_handler_f, void *);--jv jv_load_file(const char *, int);--typedef struct jv_parser jv_parser;-jv_parser* jv_parser_new(int);-void jv_parser_set_buf(jv_parser*, const char*, int, int);-int jv_parser_remaining(jv_parser*);-jv jv_parser_next(jv_parser*);-void jv_parser_free(jv_parser*);--jv jv_get(jv, jv);-jv jv_set(jv, jv, jv);-jv jv_has(jv, jv);-jv jv_setpath(jv, jv, jv);-jv jv_getpath(jv, jv);-jv jv_delpaths(jv, jv);-jv jv_keys(jv /*object or array*/);-jv jv_keys_unsorted(jv /*object or array*/);-int jv_cmp(jv, jv);-jv jv_group(jv, jv);-jv jv_sort(jv, jv);---#endif---/*--true/false/null:-check kind--number:-introduce/eliminate jv-to integer--array:-copy-free-slice-index-update--updateslice?--- */
jv_alloc.c
@@ -1,179 +0,0 @@-#include <stdlib.h>-#include <stdio.h>-#include <string.h>-#include "jv_alloc.h"--struct nomem_handler {- jv_nomem_handler_f handler;- void *data;-};--#if !defined(HAVE_PTHREAD_KEY_CREATE) || \- !defined(HAVE_PTHREAD_ONCE) || \- !defined(HAVE_ATEXIT)--/* Try thread-local storage? */--#ifdef _MSC_VER-/* Visual C++: yes */-static __declspec(thread) struct nomem_handler nomem_handler;-#define USE_TLS-#else-#ifdef HAVE___THREAD-/* GCC and friends: yes */-static __thread struct nomem_handler nomem_handler;-#define USE_TLS-#endif /* HAVE___THREAD */-#endif /* _MSC_VER */--#endif /* !HAVE_PTHREAD_KEY_CREATE */--#ifdef USE_TLS-void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {- nomem_handler.handler = handler;-}--static void memory_exhausted() {- if (nomem_handler.handler)- nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety- // Or not- fprintf(stderr, "error: cannot allocate memory\n");- abort();-}-#else /* USE_TLS */--#ifdef HAVE_PTHREAD_KEY_CREATE-#include <pthread.h>--pthread_key_t nomem_handler_key;-pthread_once_t mem_once = PTHREAD_ONCE_INIT;--static void tsd_fini(void) {- struct nomem_handler *nomem_handler;- nomem_handler = pthread_getspecific(nomem_handler_key);- if (nomem_handler) {- (void) pthread_setspecific(nomem_handler_key, NULL);- free(nomem_handler);- }-}--static void tsd_init(void) {- if (pthread_key_create(&nomem_handler_key, NULL) != 0) {- fprintf(stderr, "error: cannot create thread specific key");- abort();- }- if (atexit(tsd_fini) != 0) {- fprintf(stderr, "error: cannot set an exit handler");- abort();- }- struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));- if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {- fprintf(stderr, "error: cannot set thread specific data");- abort();- }-}--void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {- pthread_once(&mem_once, tsd_init); // cannot fail- struct nomem_handler *nomem_handler;-- nomem_handler = pthread_getspecific(nomem_handler_key);- if (nomem_handler == NULL) {- handler(data);- fprintf(stderr, "error: cannot allocate memory\n");- abort();- }- nomem_handler->handler = handler;- nomem_handler->data = data;-}--static void memory_exhausted() {- struct nomem_handler *nomem_handler;-- pthread_once(&mem_once, tsd_init);- nomem_handler = pthread_getspecific(nomem_handler_key);- if (nomem_handler)- nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety- // Or not- fprintf(stderr, "error: cannot allocate memory\n");- abort();-}--#else--/* No thread-local storage of any kind that we know how to handle */--static struct nomem_handler nomem_handler;-void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {- nomem_handler.handler = handler;- nomem_handler.data = data;-}--static void memory_exhausted() {- fprintf(stderr, "error: cannot allocate memory\n");- abort();-}--#endif /* HAVE_PTHREAD_KEY_CREATE */-#endif /* USE_TLS */---void* jv_mem_alloc(size_t sz) {- void* p = malloc(sz);- if (!p) {- memory_exhausted();- }- return p;-}--void* jv_mem_alloc_unguarded(size_t sz) {- return malloc(sz);-}--void* jv_mem_calloc(size_t nemb, size_t sz) {- void* p = calloc(nemb, sz);- if (!p) {- memory_exhausted();- }- return p;-}--void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) {- return calloc(nemb, sz);-}--char* jv_mem_strdup(const char *s) {- char *p = strdup(s);- if (!p) {- memory_exhausted();- }- return p;-}--char* jv_mem_strdup_unguarded(const char *s) {- return strdup(s);-}--void jv_mem_free(void* p) {- free(p);-}--void* jv_mem_realloc(void* p, size_t sz) {- p = realloc(p, sz);- if (!p) {- memory_exhausted();- }- return p;-}--#ifndef NDEBUG-volatile char jv_mem_uninitialised;-__attribute__((constructor)) void jv_mem_uninit_setup(){- // ignore warning that this reads uninitialized memory - that's the point!-#ifndef __clang_analyzer__- char* p = malloc(1);- jv_mem_uninitialised = *p;- free(p);-#endif-}-#endif
jv_alloc.h
@@ -1,27 +0,0 @@-#ifndef JV_ALLOC_H-#define JV_ALLOC_H--#include <stddef.h>-#include "jv.h"--#ifndef NDEBUG-extern volatile char jv_mem_uninitialised;-#endif--static void jv_mem_invalidate(void* mem, size_t n) {-#ifndef NDEBUG- char* m = mem;- while (n--) *m++ ^= jv_mem_uninitialised ^ jv_mem_uninitialised;-#endif-}--void* jv_mem_alloc(size_t);-void* jv_mem_alloc_unguarded(size_t);-void* jv_mem_calloc(size_t, size_t);-void* jv_mem_calloc_unguarded(size_t, size_t);-char* jv_mem_strdup(const char *);-char* jv_mem_strdup_unguarded(const char *);-void jv_mem_free(void*);-__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);--#endif
jv_aux.c
@@ -1,624 +0,0 @@-#include <string.h>-#include <stdlib.h>-#include <assert.h>-#include "jv_alloc.h"--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(j);- } else if (jv_get_kind(j) == JV_KIND_STRING) {- len = jv_string_length_codepoints(j);- } else {- jv_free(j);- return 0;- }- 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 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(jv_copy(t), k, &start, &end)) {- v = jv_array_slice(t, start, end);- } else {- jv_free(t);- v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));- }- } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {- int start, end;- if (parse_slice(jv_copy(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_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(jv_copy(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(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;-}--jv jv_has(jv t, jv k) {- assert(jv_is_valid(t));- assert(jv_is_valid(k));- jv ret;- if (jv_get_kind(t) == JV_KIND_NULL) {- jv_free(t);- jv_free(k);- ret = jv_false();- } else if (jv_get_kind(t) == JV_KIND_OBJECT &&- jv_get_kind(k) == JV_KIND_STRING) {- jv elem = jv_object_get(t, k);- ret = jv_bool(jv_is_valid(elem));- jv_free(elem);- } else if (jv_get_kind(t) == JV_KIND_ARRAY &&- jv_get_kind(k) == JV_KIND_NUMBER) {- jv elem = jv_array_get(t, (int)jv_number_value(k));- ret = jv_bool(jv_is_valid(elem));- jv_free(elem);- } else {- ret = jv_invalid_with_msg(jv_string_fmt("Cannot check whether %s has a %s key",- jv_kind_name(jv_get_kind(t)),- jv_kind_name(jv_get_kind(k))));- jv_free(t);- jv_free(k);- }- return ret;-}--// 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(jv_copy(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);- 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);- }- arr_out:- jv_free(starts);- jv_free(ends);- jv_free(orig_keys);- jv_free(t);- t = new_array;- } else if (jv_get_kind(t) == JV_KIND_OBJECT) {- jv_array_foreach(keys, i, k) {- if (jv_get_kind(k) != JV_KIND_STRING) {- jv_free(t);- t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",- jv_kind_name(jv_get_kind(k))));- jv_free(k);- break;- }- t = jv_object_delete(t, k);- }- } else {- jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",- jv_kind_name(jv_get_kind(t))));- jv_free(t);- t = err;- }- jv_free(keys);- return t;-}--jv jv_setpath(jv root, jv path, jv value) {- if (jv_get_kind(path) != JV_KIND_ARRAY) {- jv_free(value);- jv_free(root);- jv_free(path);- return jv_invalid_with_msg(jv_string("Path must be specified as an array"));- }- if (!jv_is_valid(root)){- jv_free(value);- jv_free(path);- return root;- }- if (jv_array_length(jv_copy(path)) == 0) {- jv_free(path);- jv_free(root);- return value;- }- jv pathcurr = jv_array_get(jv_copy(path), 0);- jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));- return jv_set(root, pathcurr,- jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));-}--jv jv_getpath(jv root, jv path) {- if (jv_get_kind(path) != JV_KIND_ARRAY) {- jv_free(root);- jv_free(path);- return jv_invalid_with_msg(jv_string("Path must be specified as an array"));- }- if (!jv_is_valid(root)) {- jv_free(path);- return root;- }- if (jv_array_length(jv_copy(path)) == 0) {- jv_free(path);- return root;- }- jv pathcurr = jv_array_get(jv_copy(path), 0);- jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));- return jv_getpath(jv_get(root, pathcurr), pathrest);-}--// assumes paths is a sorted array of arrays-static jv delpaths_sorted(jv object, jv paths, int start) {- jv delkeys = jv_array();- for (int i=0; i<jv_array_length(jv_copy(paths));) {- int j = i;- assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);- int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;- jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);- while (j < jv_array_length(jv_copy(paths)) &&- jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))- j++;- // if i <= entry < j, then entry starts with key- if (delkey) {- // deleting this entire key, we don't care about any more specific deletions- delkeys = jv_array_append(delkeys, key);- } else {- // deleting certain sub-parts of this key- jv subobject = jv_get(jv_copy(object), jv_copy(key));- if (!jv_is_valid(subobject)) {- jv_free(key);- jv_free(object);- object = subobject;- break;- } else if (jv_get_kind(subobject) == JV_KIND_NULL) {- jv_free(key);- jv_free(subobject);- } else {- jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);- if (!jv_is_valid(newsubobject)) {- jv_free(key);- jv_free(object);- object = newsubobject;- break;- }- object = jv_set(object, key, newsubobject);- }- if (!jv_is_valid(object)) break;- }- i = j;- }- jv_free(paths);- if (jv_is_valid(object))- object = jv_dels(object, delkeys);- else- jv_free(delkeys);- return object;-}--jv jv_delpaths(jv object, jv paths) {- if (jv_get_kind(paths) != JV_KIND_ARRAY) {- jv_free(object);- jv_free(paths);- return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));- }- paths = jv_sort(paths, jv_copy(paths));- jv_array_foreach(paths, i, elem) {- if (jv_get_kind(elem) != JV_KIND_ARRAY) {- jv_free(object);- jv_free(paths);- jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",- jv_kind_name(jv_get_kind(elem))));- jv_free(elem);- return err;- }- jv_free(elem);- }- if (jv_array_length(jv_copy(paths)) == 0) {- // nothing is being deleted- jv_free(paths);- return object;- }- if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {- // everything is being deleted- jv_free(paths);- jv_free(object);- return jv_null();- }- return delpaths_sorted(object, paths, 0);-}---static int string_cmp(const void* pa, const void* pb){- const jv* a = pa;- const jv* b = pb;- int lena = jv_string_length_bytes(jv_copy(*a));- int lenb = jv_string_length_bytes(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;-}--jv jv_keys_unsorted(jv x) {- if (jv_get_kind(x) != JV_KIND_OBJECT)- return jv_keys(x);- jv answer = jv_array_sized(jv_object_length(jv_copy(x)));- jv_object_foreach(x, key, value) {- answer = jv_array_append(answer, key);- jv_free(value);- }- jv_free(x);- return answer;-}--jv jv_keys(jv x) {- if (jv_get_kind(x) == JV_KIND_OBJECT) {- int nkeys = jv_object_length(jv_copy(x));- jv* keys = jv_mem_alloc(sizeof(jv) * nkeys);- int kidx = 0;- jv_object_foreach(x, key, value) {- keys[kidx++] = key;- jv_free(value);- }- qsort(keys, nkeys, sizeof(jv), string_cmp);- jv answer = jv_array_sized(nkeys);- for (int i = 0; i<nkeys; i++) {- answer = jv_array_append(answer, keys[i]);- }- jv_mem_free(keys);- jv_free(x);- return answer;- } else if (jv_get_kind(x) == JV_KIND_ARRAY) {- int n = jv_array_length(x);- jv answer = jv_array();- for (int i=0; i<n; i++){- answer = jv_array_set(answer, i, jv_number(i));- }- return answer;- } else {- assert(0 && "jv_keys passed something neither object nor array");- return jv_invalid();- }-}--int jv_cmp(jv a, jv b) {- if (jv_get_kind(a) != jv_get_kind(b)) {- int r = (int)jv_get_kind(a) - (int)jv_get_kind(b);- jv_free(a);- jv_free(b);- return r;- }- int r = 0;- switch (jv_get_kind(a)) {- default:- assert(0 && "invalid kind passed to jv_cmp");- case JV_KIND_NULL:- case JV_KIND_FALSE:- case JV_KIND_TRUE:- // there's only one of each of these values- r = 0;- break;-- case JV_KIND_NUMBER: {- double da = jv_number_value(a), db = jv_number_value(b);-- // handle NaN as though it were null- if (da != da) r = jv_cmp(jv_null(), jv_number(db));- else if (db != db) r = jv_cmp(jv_number(da), jv_null());- else if (da < db) r = -1;- else if (da == db) r = 0;- else r = 1;- break;- }-- case JV_KIND_STRING: {- r = string_cmp(&a, &b);- break;- }-- case JV_KIND_ARRAY: {- // Lexical ordering of arrays- int i = 0;- while (r == 0) {- int a_done = i >= jv_array_length(jv_copy(a));- int b_done = i >= jv_array_length(jv_copy(b));- if (a_done || b_done) {- r = b_done - a_done; //suddenly, logic- break;- }- jv xa = jv_array_get(jv_copy(a), i);- jv xb = jv_array_get(jv_copy(b), i);- r = jv_cmp(xa, xb);- i++;- }- break;- }-- case JV_KIND_OBJECT: {- jv keys_a = jv_keys(jv_copy(a));- jv keys_b = jv_keys(jv_copy(b));- r = jv_cmp(jv_copy(keys_a), keys_b);- if (r == 0) {- jv_array_foreach(keys_a, i, key) {- jv xa = jv_object_get(jv_copy(a), jv_copy(key));- jv xb = jv_object_get(jv_copy(b), key);- r = jv_cmp(xa, xb);- if (r) break;- }- }- jv_free(keys_a);- break;- }- }-- jv_free(a);- jv_free(b);- return r;-}---struct sort_entry {- jv object;- jv key;-};--static int sort_cmp(const void* pa, const void* pb) {- const struct sort_entry* a = pa;- const struct sort_entry* b = pb;- int r = jv_cmp(jv_copy(a->key), jv_copy(b->key));- // comparing by address if r == 0 makes the sort stable- return r ? r : (int)(a - b);-}--static struct sort_entry* sort_items(jv objects, jv keys) {- assert(jv_get_kind(objects) == JV_KIND_ARRAY);- assert(jv_get_kind(keys) == JV_KIND_ARRAY);- assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));- int n = jv_array_length(jv_copy(objects));- struct sort_entry* entries = jv_mem_alloc(sizeof(struct sort_entry) * n);- for (int i=0; i<n; i++) {- entries[i].object = jv_array_get(jv_copy(objects), i);- entries[i].key = jv_array_get(jv_copy(keys), i);- }- jv_free(objects);- jv_free(keys);- qsort(entries, n, sizeof(struct sort_entry), sort_cmp);- return entries;-}--jv jv_sort(jv objects, jv keys) {- assert(jv_get_kind(objects) == JV_KIND_ARRAY);- assert(jv_get_kind(keys) == JV_KIND_ARRAY);- assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));- int n = jv_array_length(jv_copy(objects));- struct sort_entry* entries = sort_items(objects, keys);- jv ret = jv_array();- for (int i=0; i<n; i++) {- jv_free(entries[i].key);- ret = jv_array_set(ret, i, entries[i].object);- }- jv_mem_free(entries);- return ret;-}--jv jv_group(jv objects, jv keys) {- assert(jv_get_kind(objects) == JV_KIND_ARRAY);- assert(jv_get_kind(keys) == JV_KIND_ARRAY);- assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));- int n = jv_array_length(jv_copy(objects));- struct sort_entry* entries = sort_items(objects, keys);- jv ret = jv_array();- if (n > 0) {- jv curr_key = entries[0].key;- jv group = jv_array_append(jv_array(), entries[0].object);- for (int i = 1; i < n; i++) {- if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {- jv_free(entries[i].key);- } else {- jv_free(curr_key);- curr_key = entries[i].key;- ret = jv_array_append(ret, group);- group = jv_array();- }- group = jv_array_append(group, entries[i].object);- }- jv_free(curr_key);- ret = jv_array_append(ret, group);- }- jv_mem_free(entries);- return ret;-}
jv_dtoa.c
@@ -1,4275 +0,0 @@--/****************************************************************- *- * The author of this software is David M. Gay.- *- * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.- *- * Permission to use, copy, modify, and distribute this software for any- * purpose without fee is hereby granted, provided that this entire notice- * is included in all copies of any software which is or includes a copy- * or modification of this software and in all copies of the supporting- * documentation for such software.- *- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED- * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY- * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.- *- ***************************************************************/--/* Please send bug reports to David M. Gay (dmg at acm dot org,- * with " at " changed at "@" and " dot " changed to "."). */--/* On a machine with IEEE extended-precision registers, it is- * necessary to specify double-precision (53-bit) rounding precision- * before invoking strtod or dtoa. If the machine uses (the equivalent- * of) Intel 80x87 arithmetic, the call- * _control87(PC_53, MCW_PC);- * does this with many compilers. Whether this or another call is- * appropriate depends on the compiler; for this to work, it may be- * necessary to #include "float.h" or another system-dependent header- * file.- */--/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.- * (Note that IEEE arithmetic is disabled by gcc's -ffast-math flag.)- *- * This strtod returns a nearest machine number to the input decimal- * string (or sets errno to ERANGE). With IEEE arithmetic, ties are- * broken by the IEEE round-even rule. Otherwise ties are broken by- * biased rounding (add half and chop).- *- * Inspired loosely by William D. Clinger's paper "How to Read Floating- * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].- *- * Modifications:- *- * 1. We only require IEEE, IBM, or VAX double-precision- * arithmetic (not IEEE double-extended).- * 2. We get by with floating-point arithmetic in a case that- * Clinger missed -- when we're computing d * 10^n- * for a small integer d and the integer n is not too- * much larger than 22 (the maximum integer k for which- * we can represent 10^k exactly), we may be able to- * compute (d*10^k) * 10^(e-k) with just one roundoff.- * 3. Rather than a bit-at-a-time adjustment of the binary- * result in the hard case, we use floating-point- * arithmetic to determine the adjustment to within- * one bit; only in really hard cases do we need to- * compute a second residual.- * 4. Because of 3., we don't need a large table of powers of 10- * for ten-to-e (just some small tables, e.g. of 10^k- * for 0 <= k <= 22).- */--/*- * #define IEEE_8087 for IEEE-arithmetic machines where the least- * significant byte has the lowest address.- * #define IEEE_MC68k for IEEE-arithmetic machines where the most- * significant byte has the lowest address.- * #define Long int on machines with 32-bit ints and 64-bit longs.- * #define IBM for IBM mainframe-style floating-point arithmetic.- * #define VAX for VAX-style floating-point arithmetic (D_floating).- * #define No_leftright to omit left-right logic in fast floating-point- * computation of dtoa. This will cause dtoa modes 4 and 5 to be- * treated the same as modes 2 and 3 for some inputs.- * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3- * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS- * is also #defined, fegetround() will be queried for the rounding mode.- * Note that both FLT_ROUNDS and fegetround() are specified by the C99- * standard (and are specified to be consistent, with fesetround()- * affecting the value of FLT_ROUNDS), but that some (Linux) systems- * do not work correctly in this regard, so using fegetround() is more- * portable than using FLT_ROUNDS directly.- * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3- * and Honor_FLT_ROUNDS is not #defined.- * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines- * that use extended-precision instructions to compute rounded- * products and quotients) with IBM.- * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic- * that rounds toward +Infinity.- * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased- * rounding when the underlying floating-point arithmetic uses- * unbiased rounding. This prevent using ordinary floating-point- * arithmetic when the result could be computed with one rounding error.- * #define Inaccurate_Divide for IEEE-format with correctly rounded- * products but inaccurate quotients, e.g., for Intel i860.- * #define NO_LONG_LONG on machines that do not have a "long long"- * integer type (of >= 64 bits). On such machines, you can- * #define Just_16 to store 16 bits per 32-bit Long when doing- * high-precision integer arithmetic. Whether this speeds things- * up or slows things down depends on the machine and the number- * being converted. If long long is available and the name is- * something other than "long long", #define Llong to be the name,- * and if "unsigned Llong" does not work as an unsigned version of- * Llong, #define #ULLong to be the corresponding unsigned type.- * #define KR_headers for old-style C function headers.- * #define Bad_float_h if your system lacks a float.h or if it does not- * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,- * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.- * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)- * if memory is available and otherwise does something you deem- * appropriate. If MALLOC is undefined, malloc will be invoked- * directly -- and assumed always to succeed. Similarly, if you- * want something other than the system's free() to be called to- * recycle memory acquired from MALLOC, #define FREE to be the- * name of the alternate routine. (FREE or free is only called in- * pathological cases, e.g., in a dtoa call after a dtoa return in- * mode 3 with thousands of digits requested.)- * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making- * memory allocations from a private pool of memory when possible.- * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,- * unless #defined to be a different length. This default length- * suffices to get rid of MALLOC calls except for unusual cases,- * such as decimal-to-binary conversion of a very long string of- * digits. The longest string dtoa can return is about 751 bytes- * long. For conversions by strtod of strings of 800 digits and- * all dtoa conversions in single-threaded executions with 8-byte- * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte- * pointers, PRIVATE_MEM >= 7112 appears adequate.- * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK- * #defined automatically on IEEE systems. On such systems,- * when INFNAN_CHECK is #defined, strtod checks- * for Infinity and NaN (case insensitively). On some systems- * (e.g., some HP systems), it may be necessary to #define NAN_WORD0- * appropriately -- to the most significant word of a quiet NaN.- * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)- * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,- * strtod also accepts (case insensitively) strings of the form- * NaN(x), where x is a string of hexadecimal digits and spaces;- * if there is only one string of hexadecimal digits, it is taken- * for the 52 fraction bits of the resulting NaN; if there are two- * or more strings of hex digits, the first is for the high 20 bits,- * the second and subsequent for the low 32 bits, with intervening- * white space ignored; but if this results in none of the 52- * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0- * and NAN_WORD1 are used instead.- * #define MULTIPLE_THREADS if the system offers preemptively scheduled- * multiple threads. In this case, you must provide (or suitably- * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed- * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed- * in pow5mult, ensures lazy evaluation of only one copy of high- * powers of 5; omitting this lock would introduce a small- * probability of wasting memory, but would otherwise be harmless.)- * You must also invoke freedtoa(s) to free the value s returned by- * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.- * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that- * avoids underflows on inputs whose result does not underflow.- * If you #define NO_IEEE_Scale on a machine that uses IEEE-format- * floating-point numbers and flushes underflows to zero rather- * than implementing gradual underflow, then you must also #define- * Sudden_Underflow.- * #define USE_LOCALE to use the current locale's decimal_point value.- * #define SET_INEXACT if IEEE arithmetic is being used and extra- * computation should be done to set the inexact flag when the- * result is inexact and avoid setting inexact when the result- * is exact. In this case, dtoa.c must be compiled in- * an environment, perhaps provided by #include "dtoa.c" in a- * suitable wrapper, that defines two functions,- * int get_inexact(void);- * void clear_inexact(void);- * such that get_inexact() returns a nonzero value if the- * inexact bit is already set, and clear_inexact() sets the- * inexact bit to 0. When SET_INEXACT is #defined, strtod- * also does extra computations to set the underflow and overflow- * flags when appropriate (i.e., when the result is tiny and- * inexact or when it is a numeric value rounded to +-infinity).- * #define NO_ERRNO if strtod should not assign errno = ERANGE when- * the result overflows to +-Infinity or underflows to 0.- * #define NO_HEX_FP to omit recognition of hexadecimal floating-point- * values by strtod.- * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now)- * to disable logic for "fast" testing of very long input strings- * to strtod. This testing proceeds by initially truncating the- * input string, then if necessary comparing the whole string with- * a decimal expansion to decide close cases. This logic is only- * used for input more than STRTOD_DIGLIM digits long (default 40).- */--#define NO_ERRNO-#define NO_HEX_FP-#define No_Hex_NaN-#define Long int--#include "jv_dtoa.h"--#include "jv_alloc.h"-#define MALLOC jv_mem_alloc-#define FREE jv_mem_free----#ifndef Long-#define Long long-#endif-#ifndef ULong-typedef unsigned Long ULong;-#endif--#ifdef DEBUG-#include "stdio.h"-#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}-#endif--#include "stdlib.h"-#include "string.h"--#ifdef USE_LOCALE-#include "locale.h"-#endif--#ifdef Honor_FLT_ROUNDS-#ifndef Trust_FLT_ROUNDS-#include <fenv.h>-#endif-#endif--#ifdef MALLOC-extern void *MALLOC(size_t);-#else-#define MALLOC malloc-#endif--#undef IEEE_Arith-#undef Avoid_Underflow-#ifdef IEEE_MC68k-#define IEEE_Arith-#endif-#ifdef IEEE_8087-#define IEEE_Arith-#endif--#ifdef IEEE_Arith-#ifndef NO_INFNAN_CHECK-#undef INFNAN_CHECK-#define INFNAN_CHECK-#endif-#else-#undef INFNAN_CHECK-#define NO_STRTOD_BIGCOMP-#endif--#include "errno.h"--#ifdef Bad_float_h--#ifdef IEEE_Arith-#define DBL_DIG 15-#define DBL_MAX_10_EXP 308-#define DBL_MAX_EXP 1024-#define FLT_RADIX 2-#endif /*IEEE_Arith*/--#ifdef IBM-#define DBL_DIG 16-#define DBL_MAX_10_EXP 75-#define DBL_MAX_EXP 63-#define FLT_RADIX 16-#define DBL_MAX 7.2370055773322621e+75-#endif--#ifdef VAX-#define DBL_DIG 16-#define DBL_MAX_10_EXP 38-#define DBL_MAX_EXP 127-#define FLT_RADIX 2-#define DBL_MAX 1.7014118346046923e+38-#endif--#ifndef LONG_MAX-#define LONG_MAX 2147483647-#endif--#else /* ifndef Bad_float_h */-#include "float.h"-#endif /* Bad_float_h */--#ifndef __MATH_H__-#include "math.h"-#endif--#ifdef __cplusplus-extern "C" {-#endif--#ifndef CONST-#define CONST const-#endif--#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1-Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.-#endif--typedef union { double d; ULong L[2]; } U;--#ifdef IEEE_8087-#define word0(x) (x)->L[1]-#define word1(x) (x)->L[0]-#else-#define word0(x) (x)->L[0]-#define word1(x) (x)->L[1]-#endif-#define dval(x) (x)->d--#ifndef STRTOD_DIGLIM-#define STRTOD_DIGLIM 40-#endif--#ifdef DIGLIM_DEBUG-extern int strtod_diglim;-#else-#define strtod_diglim STRTOD_DIGLIM-#endif--/* The following definition of Storeinc is appropriate for MIPS processors.- * An alternative that might be better on some machines is- * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)- */-#if defined(IEEE_8087) + defined(VAX)-#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \-((unsigned short *)a)[0] = (unsigned short)c, a++)-#else-#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \-((unsigned short *)a)[1] = (unsigned short)c, a++)-#endif--/* #define P DBL_MANT_DIG */-/* Ten_pmax = floor(P*log(2)/log(5)) */-/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */-/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */-/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */--#ifdef IEEE_Arith-#define Exp_shift 20-#define Exp_shift1 20-#define Exp_msk1 0x100000-#define Exp_msk11 0x100000-#define Exp_mask 0x7ff00000-#define P 53-#define Nbits 53-#define Bias 1023-#define Emax 1023-#define Emin (-1022)-#define Exp_1 0x3ff00000-#define Exp_11 0x3ff00000-#define Ebits 11-#define Frac_mask 0xfffff-#define Frac_mask1 0xfffff-#define Ten_pmax 22-#define Bletch 0x10-#define Bndry_mask 0xfffff-#define Bndry_mask1 0xfffff-#define LSB 1-#define Sign_bit 0x80000000-#define Log2P 1-#define Tiny0 0-#define Tiny1 1-#define Quick_max 14-#define Int_max 14-#ifndef NO_IEEE_Scale-#define Avoid_Underflow-#ifdef Flush_Denorm /* debugging option */-#undef Sudden_Underflow-#endif-#endif--#ifndef Flt_Rounds-#ifdef FLT_ROUNDS-#define Flt_Rounds FLT_ROUNDS-#else-#define Flt_Rounds 1-#endif-#endif /*Flt_Rounds*/--#ifdef Honor_FLT_ROUNDS-#undef Check_FLT_ROUNDS-#define Check_FLT_ROUNDS-#else-#define Rounding Flt_Rounds-#endif--#else /* ifndef IEEE_Arith */-#undef Check_FLT_ROUNDS-#undef Honor_FLT_ROUNDS-#undef SET_INEXACT-#undef Sudden_Underflow-#define Sudden_Underflow-#ifdef IBM-#undef Flt_Rounds-#define Flt_Rounds 0-#define Exp_shift 24-#define Exp_shift1 24-#define Exp_msk1 0x1000000-#define Exp_msk11 0x1000000-#define Exp_mask 0x7f000000-#define P 14-#define Nbits 56-#define Bias 65-#define Emax 248-#define Emin (-260)-#define Exp_1 0x41000000-#define Exp_11 0x41000000-#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */-#define Frac_mask 0xffffff-#define Frac_mask1 0xffffff-#define Bletch 4-#define Ten_pmax 22-#define Bndry_mask 0xefffff-#define Bndry_mask1 0xffffff-#define LSB 1-#define Sign_bit 0x80000000-#define Log2P 4-#define Tiny0 0x100000-#define Tiny1 0-#define Quick_max 14-#define Int_max 15-#else /* VAX */-#undef Flt_Rounds-#define Flt_Rounds 1-#define Exp_shift 23-#define Exp_shift1 7-#define Exp_msk1 0x80-#define Exp_msk11 0x800000-#define Exp_mask 0x7f80-#define P 56-#define Nbits 56-#define Bias 129-#define Emax 126-#define Emin (-129)-#define Exp_1 0x40800000-#define Exp_11 0x4080-#define Ebits 8-#define Frac_mask 0x7fffff-#define Frac_mask1 0xffff007f-#define Ten_pmax 24-#define Bletch 2-#define Bndry_mask 0xffff007f-#define Bndry_mask1 0xffff007f-#define LSB 0x10000-#define Sign_bit 0x8000-#define Log2P 1-#define Tiny0 0x80-#define Tiny1 0-#define Quick_max 15-#define Int_max 15-#endif /* IBM, VAX */-#endif /* IEEE_Arith */--#ifndef IEEE_Arith-#define ROUND_BIASED-#else-#ifdef ROUND_BIASED_without_Round_Up-#undef ROUND_BIASED-#define ROUND_BIASED-#endif-#endif--#ifdef RND_PRODQUOT-#define rounded_product(a,b) a = rnd_prod(a, b)-#define rounded_quotient(a,b) a = rnd_quot(a, b)-extern double rnd_prod(double, double), rnd_quot(double, double);-#else-#define rounded_product(a,b) a *= b-#define rounded_quotient(a,b) a /= b-#endif--#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))-#define Big1 0xffffffff--#ifndef Pack_32-#define Pack_32-#endif--typedef struct BCinfo BCinfo;- struct-BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; };--#define FFFFFFFF 0xffffffffUL--#ifdef NO_LONG_LONG-#undef ULLong-#ifdef Just_16-#undef Pack_32-/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.- * This makes some inner loops simpler and sometimes saves work- * during multiplications, but it often seems to make things slightly- * slower. Hence the default is now to store 32 bits per Long.- */-#endif-#else /* long long available */-#ifndef Llong-#define Llong long long-#endif-#ifndef ULLong-#define ULLong unsigned Llong-#endif-#endif /* NO_LONG_LONG */---- struct-Bigint {- struct Bigint *next;- int k, maxwds, sign, wds;- ULong x[1];- };-- typedef struct Bigint Bigint;---void jvp_dtoa_context_init(struct dtoa_context* C) {- int i;- for (i=0; i<(int)(sizeof(C->freelist)/sizeof(C->freelist[0])); i++) {- C->freelist[i] = 0;- }- C->p5s = 0;-}-- static Bigint *- Balloc(struct dtoa_context* C, int k)-{- int x;- Bigint *rv;-- /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */- /* but this case seems very unlikely. */- if (k <= Kmax && (rv = C->freelist[k]))- C->freelist[k] = rv->next;- else {- x = 1 << k;- rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));- rv->k = k;- rv->maxwds = x;- }- rv->sign = rv->wds = 0;- return rv;- }-- static void-Bfree- (struct dtoa_context* C, Bigint *v)-{- if (v) {- if (v->k > Kmax)-#ifdef FREE- FREE((void*)v);-#else- free((void*)v);-#endif- else {- v->next = C->freelist[v->k];- C->freelist[v->k] = v;- }- }- }---void jvp_dtoa_context_free(struct dtoa_context* C) {- int k;- while (C->p5s) {- Bigint* p5 = C->p5s;- C->p5s = p5->next;- Bfree(C, p5);- }- for (k=0; k<(int)(sizeof(C->freelist)/sizeof(C->freelist[0])); k++) {- while (C->freelist[k]) {- Bigint* v = C->freelist[k];- C->freelist[k] = v->next;- FREE(v);- }- }-}---#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \-y->wds*sizeof(Long) + 2*sizeof(int))-- static Bigint *-multadd- (struct dtoa_context* C, Bigint *b, int m, int a) /* multiply by m and add a */-{- int i, wds;-#ifdef ULLong- ULong *x;- ULLong carry, y;-#else- ULong carry, *x, y;-#ifdef Pack_32- ULong xi, z;-#endif-#endif- Bigint *b1;-- wds = b->wds;- x = b->x;- i = 0;- carry = a;- do {-#ifdef ULLong- y = *x * (ULLong)m + carry;- carry = y >> 32;- *x++ = y & FFFFFFFF;-#else-#ifdef Pack_32- xi = *x;- y = (xi & 0xffff) * m + carry;- z = (xi >> 16) * m + (y >> 16);- carry = z >> 16;- *x++ = (z << 16) + (y & 0xffff);-#else- y = *x * m + carry;- carry = y >> 16;- *x++ = y & 0xffff;-#endif-#endif- }- while(++i < wds);- if (carry) {- if (wds >= b->maxwds) {- b1 = Balloc(C, b->k+1);- Bcopy(b1, b);- Bfree(C, b);- b = b1;- }- b->x[wds++] = carry;- b->wds = wds;- }- return b;- }-- static Bigint *-s2b- (struct dtoa_context* C, const char *s, int nd0, int nd, ULong y9, int dplen)-{- Bigint *b;- int i, k;- Long x, y;-- x = (nd + 8) / 9;- for(k = 0, y = 1; x > y; y <<= 1, k++) ;-#ifdef Pack_32- b = Balloc(C, k);- b->x[0] = y9;- b->wds = 1;-#else- b = Balloc(C, k+1);- b->x[0] = y9 & 0xffff;- b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;-#endif-- i = 9;- if (9 < nd0) {- s += 9;- do b = multadd(C, b, 10, *s++ - '0');- while(++i < nd0);- s += dplen;- }- else- s += dplen + 9;- for(; i < nd; i++)- b = multadd(C, b, 10, *s++ - '0');- return b;- }-- static int-hi0bits- (struct dtoa_context* C, ULong x)-{- int k = 0;-- if (!(x & 0xffff0000)) {- k = 16;- x <<= 16;- }- if (!(x & 0xff000000)) {- k += 8;- x <<= 8;- }- if (!(x & 0xf0000000)) {- k += 4;- x <<= 4;- }- if (!(x & 0xc0000000)) {- k += 2;- x <<= 2;- }- if (!(x & 0x80000000)) {- k++;- if (!(x & 0x40000000))- return 32;- }- return k;- }-- static int-lo0bits- (struct dtoa_context* C, ULong *y)-{- int k;- ULong x = *y;-- if (x & 7) {- if (x & 1)- return 0;- if (x & 2) {- *y = x >> 1;- return 1;- }- *y = x >> 2;- return 2;- }- k = 0;- if (!(x & 0xffff)) {- k = 16;- x >>= 16;- }- if (!(x & 0xff)) {- k += 8;- x >>= 8;- }- if (!(x & 0xf)) {- k += 4;- x >>= 4;- }- if (!(x & 0x3)) {- k += 2;- x >>= 2;- }- if (!(x & 1)) {- k++;- x >>= 1;- if (!x)- return 32;- }- *y = x;- return k;- }-- static Bigint *-i2b- (struct dtoa_context* C, int i)-{- Bigint *b;-- b = Balloc(C, 1);- b->x[0] = i;- b->wds = 1;- return b;- }-- static Bigint *-mult- (struct dtoa_context* C, Bigint *a, Bigint *b)-{- Bigint *c;- int k, wa, wb, wc;- ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;- ULong y;-#ifdef ULLong- ULLong carry, z;-#else- ULong carry, z;-#ifdef Pack_32- ULong z2;-#endif-#endif-- if (a->wds < b->wds) {- c = a;- a = b;- b = c;- }- k = a->k;- wa = a->wds;- wb = b->wds;- wc = wa + wb;- if (wc > a->maxwds)- k++;- c = Balloc(C, k);- for(x = c->x, xa = x + wc; x < xa; x++)- *x = 0;- xa = a->x;- xae = xa + wa;- xb = b->x;- xbe = xb + wb;- xc0 = c->x;-#ifdef ULLong- for(; xb < xbe; xc0++) {- if ((y = *xb++)) {- x = xa;- xc = xc0;- carry = 0;- do {- z = *x++ * (ULLong)y + *xc + carry;- carry = z >> 32;- *xc++ = z & FFFFFFFF;- }- while(x < xae);- *xc = carry;- }- }-#else-#ifdef Pack_32- for(; xb < xbe; xb++, xc0++) {- if (y = *xb & 0xffff) {- x = xa;- xc = xc0;- carry = 0;- do {- z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;- carry = z >> 16;- z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;- carry = z2 >> 16;- Storeinc(xc, z2, z);- }- while(x < xae);- *xc = carry;- }- if (y = *xb >> 16) {- x = xa;- xc = xc0;- carry = 0;- z2 = *xc;- do {- z = (*x & 0xffff) * y + (*xc >> 16) + carry;- carry = z >> 16;- Storeinc(xc, z, z2);- z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;- carry = z2 >> 16;- }- while(x < xae);- *xc = z2;- }- }-#else- for(; xb < xbe; xc0++) {- if (y = *xb++) {- x = xa;- xc = xc0;- carry = 0;- do {- z = *x++ * y + *xc + carry;- carry = z >> 16;- *xc++ = z & 0xffff;- }- while(x < xae);- *xc = carry;- }- }-#endif-#endif- for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;- c->wds = wc;- return c;- }--- static Bigint *-pow5mult- (struct dtoa_context* C, Bigint *b, int k)-{- Bigint *b1, *p5, *p51;- int i;- static const int p05[3] = { 5, 25, 125 };-- if ((i = k & 3))- b = multadd(C, b, p05[i-1], 0);-- if (!(k >>= 2))- return b;- if (!(p5 = C->p5s)) {- /* first time */- p5 = C->p5s = i2b(C, 625);- p5->next = 0;- }- for(;;) {- if (k & 1) {- b1 = mult(C, b, p5);- Bfree(C, b);- b = b1;- }- if (!(k >>= 1))- break;- if (!(p51 = p5->next)) {- p51 = p5->next = mult(C, p5,p5);- p51->next = 0;- }- p5 = p51;- }- return b;- }-- static Bigint *-lshift- (struct dtoa_context* C, Bigint *b, int k)-{- int i, k1, n, n1;- Bigint *b1;- ULong *x, *x1, *xe, z;--#ifdef Pack_32- n = k >> 5;-#else- n = k >> 4;-#endif- k1 = b->k;- n1 = n + b->wds + 1;- for(i = b->maxwds; n1 > i; i <<= 1)- k1++;- b1 = Balloc(C, k1);- x1 = b1->x;- for(i = 0; i < n; i++)- *x1++ = 0;- x = b->x;- xe = x + b->wds;-#ifdef Pack_32- if (k &= 0x1f) {- k1 = 32 - k;- z = 0;- do {- *x1++ = *x << k | z;- z = *x++ >> k1;- }- while(x < xe);- if ((*x1 = z))- ++n1;- }-#else- if (k &= 0xf) {- k1 = 16 - k;- z = 0;- do {- *x1++ = *x << k & 0xffff | z;- z = *x++ >> k1;- }- while(x < xe);- if (*x1 = z)- ++n1;- }-#endif- else do- *x1++ = *x++;- while(x < xe);- b1->wds = n1 - 1;- Bfree(C, b);- return b1;- }-- static int-cmp- (struct dtoa_context* C, Bigint *a, Bigint *b)-{- ULong *xa, *xa0, *xb, *xb0;- int i, j;-- i = a->wds;- j = b->wds;-#ifdef DEBUG- if (i > 1 && !a->x[i-1])- Bug("cmp called with a->x[a->wds-1] == 0");- if (j > 1 && !b->x[j-1])- Bug("cmp called with b->x[b->wds-1] == 0");-#endif- if (i -= j)- return i;- xa0 = a->x;- xa = xa0 + j;- xb0 = b->x;- xb = xb0 + j;- for(;;) {- if (*--xa != *--xb)- return *xa < *xb ? -1 : 1;- if (xa <= xa0)- break;- }- return 0;- }-- static Bigint *-diff- (struct dtoa_context* C, Bigint *a, Bigint *b)-{- Bigint *c;- int i, wa, wb;- ULong *xa, *xae, *xb, *xbe, *xc;-#ifdef ULLong- ULLong borrow, y;-#else- ULong borrow, y;-#ifdef Pack_32- ULong z;-#endif-#endif-- i = cmp(C, a,b);- if (!i) {- c = Balloc(C, 0);- c->wds = 1;- c->x[0] = 0;- return c;- }- if (i < 0) {- c = a;- a = b;- b = c;- i = 1;- }- else- i = 0;- c = Balloc(C, a->k);- c->sign = i;- wa = a->wds;- xa = a->x;- xae = xa + wa;- wb = b->wds;- xb = b->x;- xbe = xb + wb;- xc = c->x;- borrow = 0;-#ifdef ULLong- do {- y = (ULLong)*xa++ - *xb++ - borrow;- borrow = y >> 32 & (ULong)1;- *xc++ = y & FFFFFFFF;- }- while(xb < xbe);- while(xa < xae) {- y = *xa++ - borrow;- borrow = y >> 32 & (ULong)1;- *xc++ = y & FFFFFFFF;- }-#else-#ifdef Pack_32- do {- y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;- borrow = (z & 0x10000) >> 16;- Storeinc(xc, z, y);- }- while(xb < xbe);- while(xa < xae) {- y = (*xa & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- z = (*xa++ >> 16) - borrow;- borrow = (z & 0x10000) >> 16;- Storeinc(xc, z, y);- }-#else- do {- y = *xa++ - *xb++ - borrow;- borrow = (y & 0x10000) >> 16;- *xc++ = y & 0xffff;- }- while(xb < xbe);- while(xa < xae) {- y = *xa++ - borrow;- borrow = (y & 0x10000) >> 16;- *xc++ = y & 0xffff;- }-#endif-#endif- while(!*--xc)- wa--;- c->wds = wa;- return c;- }-- static double-ulp- (struct dtoa_context* C, U *x)-{- Long L;- U u;-- L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;-#ifndef Avoid_Underflow-#ifndef Sudden_Underflow- if (L > 0) {-#endif-#endif-#ifdef IBM- L |= Exp_msk1 >> 4;-#endif- word0(&u) = L;- word1(&u) = 0;-#ifndef Avoid_Underflow-#ifndef Sudden_Underflow- }- else {- L = -L >> Exp_shift;- if (L < Exp_shift) {- word0(&u) = 0x80000 >> L;- word1(&u) = 0;- }- else {- word0(&u) = 0;- L -= Exp_shift;- word1(&u) = L >= 31 ? 1 : 1 << 31 - L;- }- }-#endif-#endif- return dval(&u);- }-- static double-b2d- (struct dtoa_context* C, Bigint *a, int *e)-{- ULong *xa, *xa0, w, y, z;- int k;- U d;-#ifdef VAX- ULong d0, d1;-#else-#define d0 word0(&d)-#define d1 word1(&d)-#endif-- xa0 = a->x;- xa = xa0 + a->wds;- y = *--xa;-#ifdef DEBUG- if (!y) Bug("zero y in b2d");-#endif- k = hi0bits(C, y);- *e = 32 - k;-#ifdef Pack_32- if (k < Ebits) {- d0 = Exp_1 | y >> (Ebits - k);- w = xa > xa0 ? *--xa : 0;- d1 = y << ((32-Ebits) + k) | w >> (Ebits - k);- goto ret_d;- }- z = xa > xa0 ? *--xa : 0;- if (k -= Ebits) {- d0 = Exp_1 | y << k | z >> (32 - k);- y = xa > xa0 ? *--xa : 0;- d1 = z << k | y >> (32 - k);- }- else {- d0 = Exp_1 | y;- d1 = z;- }-#else- if (k < Ebits + 16) {- z = xa > xa0 ? *--xa : 0;- d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;- w = xa > xa0 ? *--xa : 0;- y = xa > xa0 ? *--xa : 0;- d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;- goto ret_d;- }- z = xa > xa0 ? *--xa : 0;- w = xa > xa0 ? *--xa : 0;- k -= Ebits + 16;- d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;- y = xa > xa0 ? *--xa : 0;- d1 = w << k + 16 | y << k;-#endif- ret_d:-#ifdef VAX- word0(&d) = d0 >> 16 | d0 << 16;- word1(&d) = d1 >> 16 | d1 << 16;-#else-#undef d0-#undef d1-#endif- return dval(&d);- }-- static Bigint *-d2b- (struct dtoa_context* C, U *d, int *e, int *bits)-{- Bigint *b;- int de, k;- ULong *x, y, z;-#ifndef Sudden_Underflow- int i;-#endif-#ifdef VAX- ULong d0, d1;- d0 = word0(d) >> 16 | word0(d) << 16;- d1 = word1(d) >> 16 | word1(d) << 16;-#else-#define d0 word0(d)-#define d1 word1(d)-#endif--#ifdef Pack_32- b = Balloc(C, 1);-#else- b = Balloc(C, 2);-#endif- x = b->x;-- z = d0 & Frac_mask;- d0 &= 0x7fffffff; /* clear sign bit, which we ignore */-#ifdef Sudden_Underflow- de = (int)(d0 >> Exp_shift);-#ifndef IBM- z |= Exp_msk11;-#endif-#else- if ((de = (int)(d0 >> Exp_shift)))- z |= Exp_msk1;-#endif-#ifdef Pack_32- if ((y = d1)) {- if ((k = lo0bits(C, &y))) {- x[0] = y | z << (32 - k);- z >>= k;- }- else- x[0] = y;-#ifndef Sudden_Underflow- i =-#endif- b->wds = (x[1] = z) ? 2 : 1;- }- else {- k = lo0bits(C, &z);- x[0] = z;-#ifndef Sudden_Underflow- i =-#endif- b->wds = 1;- k += 32;- }-#else- if (y = d1) {- if (k = lo0bits(C, &y))- if (k >= 16) {- x[0] = y | z << 32 - k & 0xffff;- x[1] = z >> k - 16 & 0xffff;- x[2] = z >> k;- i = 2;- }- else {- x[0] = y & 0xffff;- x[1] = y >> 16 | z << 16 - k & 0xffff;- x[2] = z >> k & 0xffff;- x[3] = z >> k+16;- i = 3;- }- else {- x[0] = y & 0xffff;- x[1] = y >> 16;- x[2] = z & 0xffff;- x[3] = z >> 16;- i = 3;- }- }- else {-#ifdef DEBUG- if (!z)- Bug("Zero passed to d2b");-#endif- k = lo0bits(C, &z);- if (k >= 16) {- x[0] = z;- i = 0;- }- else {- x[0] = z & 0xffff;- x[1] = z >> 16;- i = 1;- }- k += 32;- }- while(!x[i])- --i;- b->wds = i + 1;-#endif-#ifndef Sudden_Underflow- if (de) {-#endif-#ifdef IBM- *e = (de - Bias - (P-1) << 2) + k;- *bits = 4*P + 8 - k - hi0bits(C, word0(d) & Frac_mask);-#else- *e = de - Bias - (P-1) + k;- *bits = P - k;-#endif-#ifndef Sudden_Underflow- }- else {- *e = de - Bias - (P-1) + 1 + k;-#ifdef Pack_32- *bits = 32*i - hi0bits(C, x[i-1]);-#else- *bits = (i+2)*16 - hi0bits(C, x[i]);-#endif- }-#endif- return b;- }-#undef d0-#undef d1-- static double-ratio- (struct dtoa_context* C, Bigint *a, Bigint *b)-{- U da, db;- int k, ka, kb;-- dval(&da) = b2d(C, a, &ka);- dval(&db) = b2d(C, b, &kb);-#ifdef Pack_32- k = ka - kb + 32*(a->wds - b->wds);-#else- k = ka - kb + 16*(a->wds - b->wds);-#endif-#ifdef IBM- if (k > 0) {- word0(&da) += (k >> 2)*Exp_msk1;- if (k &= 3)- dval(&da) *= 1 << k;- }- else {- k = -k;- word0(&db) += (k >> 2)*Exp_msk1;- if (k &= 3)- dval(&db) *= 1 << k;- }-#else- if (k > 0)- word0(&da) += k*Exp_msk1;- else {- k = -k;- word0(&db) += k*Exp_msk1;- }-#endif- return dval(&da) / dval(&db);- }-- static CONST double-tens[] = {- 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,- 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,- 1e20, 1e21, 1e22-#ifdef VAX- , 1e23, 1e24-#endif- };-- static CONST double-#ifdef IEEE_Arith-bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };-static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,-#ifdef Avoid_Underflow- 9007199254740992.*9007199254740992.e-256- /* = 2^106 * 1e-256 */-#else- 1e-256-#endif- };-/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */-/* flag unnecessarily. It leads to a song and dance at the end of strtod. */-#define Scale_Bit 0x10-#define n_bigtens 5-#else-#ifdef IBM-bigtens[] = { 1e16, 1e32, 1e64 };-static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };-#define n_bigtens 3-#else-bigtens[] = { 1e16, 1e32 };-static CONST double tinytens[] = { 1e-16, 1e-32 };-#define n_bigtens 2-#endif-#endif--#undef Need_Hexdig-#ifdef INFNAN_CHECK-#ifndef No_Hex_NaN-#define Need_Hexdig-#endif-#endif--#ifndef Need_Hexdig-#ifndef NO_HEX_FP-#define Need_Hexdig-#endif-#endif--#ifdef Need_Hexdig /*{*/-static unsigned char hexdig[256];-- static void-htinit(unsigned char *h, unsigned char *s, int inc)-{- int i, j;- for(i = 0; (j = s[i]) !=0; i++)- h[j] = i + inc;- }-- static void-hexdig_init(void)-{-#define USC (unsigned char *)- htinit(hexdig, USC "0123456789", 0x10);- htinit(hexdig, USC "abcdef", 0x10 + 10);- htinit(hexdig, USC "ABCDEF", 0x10 + 10);- }-#endif /* } Need_Hexdig */--#ifdef INFNAN_CHECK--#ifndef NAN_WORD0-#define NAN_WORD0 0x7ff80000-#endif--#ifndef NAN_WORD1-#define NAN_WORD1 0-#endif-- static int-match- (struct dtoa_context* C, const char **sp, const char *t)-{- int c, d;- CONST char *s = *sp;-- while((d = *t++)) {- if ((c = *++s) >= 'A' && c <= 'Z')- c += 'a' - 'A';- if (c != d)- return 0;- }- *sp = s + 1;- return 1;- }--#ifndef No_Hex_NaN- static void-hexnan- (struct dtoa_context* C, U *rvp, const char **sp)-{- ULong c, x[2];- CONST char *s;- int c1, havedig, udx0, xshift;-- if (!hexdig['0'])- hexdig_init();- x[0] = x[1] = 0;- havedig = xshift = 0;- udx0 = 1;- s = *sp;- /* allow optional initial 0x or 0X */- while((c = *(CONST unsigned char*)(s+1)) && c <= ' ')- ++s;- if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X'))- s += 2;- while((c = *(CONST unsigned char*)++s)) {- if ((c1 = hexdig[c]))- c = c1 & 0xf;- else if (c <= ' ') {- if (udx0 && havedig) {- udx0 = 0;- xshift = 1;- }- continue;- }-#ifdef GDTOA_NON_PEDANTIC_NANCHECK- else if (/*(*/ c == ')' && havedig) {- *sp = s + 1;- break;- }- else- return; /* invalid form: don't change *sp */-#else- else {- do {- if (/*(*/ c == ')') {- *sp = s + 1;- break;- }- } while((c = *++s));- break;- }-#endif- havedig = 1;- if (xshift) {- xshift = 0;- x[0] = x[1];- x[1] = 0;- }- if (udx0)- x[0] = (x[0] << 4) | (x[1] >> 28);- x[1] = (x[1] << 4) | c;- }- if ((x[0] &= 0xfffff) || x[1]) {- word0(rvp) = Exp_mask | x[0];- word1(rvp) = x[1];- }- }-#endif /*No_Hex_NaN*/-#endif /* INFNAN_CHECK */--#ifdef Pack_32-#define ULbits 32-#define kshift 5-#define kmask 31-#else-#define ULbits 16-#define kshift 4-#define kmask 15-#endif--#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/- static Bigint *-increment(struct dtoa_context* C, Bigint *b)-{- ULong *x, *xe;- Bigint *b1;-- x = b->x;- xe = x + b->wds;- do {- if (*x < (ULong)0xffffffffL) {- ++*x;- return b;- }- *x++ = 0;- } while(x < xe);- {- if (b->wds >= b->maxwds) {- b1 = Balloc(C, b->k+1);- Bcopy(b1,b);- Bfree(C, b);- b = b1;- }- b->x[b->wds++] = 1;- }- return b;- }--#endif /*}*/--#ifndef NO_HEX_FP /*{*/-- static void-rshift(struct dtoa_context* C, Bigint *b, int k)-{- ULong *x, *x1, *xe, y;- int n;-- x = x1 = b->x;- n = k >> kshift;- if (n < b->wds) {- xe = x + b->wds;- x += n;- if (k &= kmask) {- n = 32 - k;- y = *x++ >> k;- while(x < xe) {- *x1++ = (y | (*x << n)) & 0xffffffff;- y = *x++ >> k;- }- if ((*x1 = y) !=0)- x1++;- }- else- while(x < xe)- *x1++ = *x++;- }- if ((b->wds = x1 - b->x) == 0)- b->x[0] = 0;- }-- static ULong-any_on(Bigint *b, int k)-{- int n, nwds;- ULong *x, *x0, x1, x2;-- x = b->x;- nwds = b->wds;- n = k >> kshift;- if (n > nwds)- n = nwds;- else if (n < nwds && (k &= kmask)) {- x1 = x2 = x[n];- x1 >>= k;- x1 <<= k;- if (x1 != x2)- return 1;- }- x0 = x;- x += n;- while(x > x0)- if (*--x)- return 1;- return 0;- }--enum { /* rounding values: same as FLT_ROUNDS */- Round_zero = 0,- Round_near = 1,- Round_up = 2,- Round_down = 3- };--static void-gethex(struct dtoa_context* C, CONST char **sp, U *rvp, int rounding, int sign)-{- Bigint *b;- CONST unsigned char *decpt, *s0, *s, *s1;- Long e, e1;- ULong L, lostbits, *x;- int big, denorm, esign, havedig, k, n, nbits, up, zret;-#ifdef IBM- int j;-#endif- enum {-#ifdef IEEE_Arith /*{{*/- emax = 0x7fe - Bias - P + 1,- emin = Emin - P + 1-#else /*}{*/- emin = Emin - P,-#ifdef VAX- emax = 0x7ff - Bias - P + 1-#endif-#ifdef IBM- emax = 0x7f - Bias - P-#endif-#endif /*}}*/- };-#ifdef USE_LOCALE- int i;-#ifdef NO_LOCALE_CACHE- const unsigned char *decimalpoint = (unsigned char*)- localeconv()->decimal_point;-#else- const unsigned char *decimalpoint;- static unsigned char *decimalpoint_cache;- if (!(s0 = decimalpoint_cache)) {- s0 = (unsigned char*)localeconv()->decimal_point;- if ((decimalpoint_cache = (unsigned char*)- MALLOC(strlen((CONST char*)s0) + 1))) {- strcpy((char*)decimalpoint_cache, (CONST char*)s0);- s0 = decimalpoint_cache;- }- }- decimalpoint = s0;-#endif-#endif-- if (!hexdig['0'])- hexdig_init();- havedig = 0;- s0 = *(CONST unsigned char **)sp + 2;- while(s0[havedig] == '0')- havedig++;- s0 += havedig;- s = s0;- decpt = 0;- zret = 0;- e = 0;- if (hexdig[*s])- havedig++;- else {- zret = 1;-#ifdef USE_LOCALE- for(i = 0; decimalpoint[i]; ++i) {- if (s[i] != decimalpoint[i])- goto pcheck;- }- decpt = s += i;-#else- if (*s != '.')- goto pcheck;- decpt = ++s;-#endif- if (!hexdig[*s])- goto pcheck;- while(*s == '0')- s++;- if (hexdig[*s])- zret = 0;- havedig = 1;- s0 = s;- }- while(hexdig[*s])- s++;-#ifdef USE_LOCALE- if (*s == *decimalpoint && !decpt) {- for(i = 1; decimalpoint[i]; ++i) {- if (s[i] != decimalpoint[i])- goto pcheck;- }- decpt = s += i;-#else- if (*s == '.' && !decpt) {- decpt = ++s;-#endif- while(hexdig[*s])- s++;- }/*}*/- if (decpt)- e = -(((Long)(s-decpt)) << 2);- pcheck:- s1 = s;- big = esign = 0;- switch(*s) {- case 'p':- case 'P':- switch(*++s) {- case '-':- esign = 1;- /* no break */- case '+':- s++;- }- if ((n = hexdig[*s]) == 0 || n > 0x19) {- s = s1;- break;- }- e1 = n - 0x10;- while((n = hexdig[*++s]) !=0 && n <= 0x19) {- if (e1 & 0xf8000000)- big = 1;- e1 = 10*e1 + n - 0x10;- }- if (esign)- e1 = -e1;- e += e1;- }- *sp = (char*)s;- if (!havedig)- *sp = (char*)s0 - 1;- if (zret)- goto retz1;- if (big) {- if (esign) {-#ifdef IEEE_Arith- switch(rounding) {- case Round_up:- if (sign)- break;- goto ret_tiny;- case Round_down:- if (!sign)- break;- goto ret_tiny;- }-#endif- goto retz;-#ifdef IEEE_Arith- ret_tiny:-#ifndef NO_ERRNO- errno = ERANGE;-#endif- word0(rvp) = 0;- word1(rvp) = 1;- return;-#endif /* IEEE_Arith */- }- switch(rounding) {- case Round_near:- goto ovfl1;- case Round_up:- if (!sign)- goto ovfl1;- goto ret_big;- case Round_down:- if (sign)- goto ovfl1;- goto ret_big;- }- ret_big:- word0(rvp) = Big0;- word1(rvp) = Big1;- return;- }- n = s1 - s0 - 1;- for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)- k++;- b = Balloc(C, k);- x = b->x;- n = 0;- L = 0;-#ifdef USE_LOCALE- for(i = 0; decimalpoint[i+1]; ++i);-#endif- while(s1 > s0) {-#ifdef USE_LOCALE- if (*--s1 == decimalpoint[i]) {- s1 -= i;- continue;- }-#else- if (*--s1 == '.')- continue;-#endif- if (n == ULbits) {- *x++ = L;- L = 0;- n = 0;- }- L |= (hexdig[*s1] & 0x0f) << n;- n += 4;- }- *x++ = L;- b->wds = n = x - b->x;- n = ULbits*n - hi0bits(C, L);- nbits = Nbits;- lostbits = 0;- x = b->x;- if (n > nbits) {- n -= nbits;- if (any_on(b,n)) {- lostbits = 1;- k = n - 1;- if (x[k>>kshift] & 1 << (k & kmask)) {- lostbits = 2;- if (k > 0 && any_on(b,k))- lostbits = 3;- }- }- rshift(C, b, n);- e += n;- }- else if (n < nbits) {- n = nbits - n;- b = lshift(C, b, n);- e -= n;- x = b->x;- }- if (e > Emax) {- ovfl:- Bfree(C, b);- ovfl1:-#ifndef NO_ERRNO- errno = ERANGE;-#endif- word0(rvp) = Exp_mask;- word1(rvp) = 0;- return;- }- denorm = 0;- if (e < emin) {- denorm = 1;- n = emin - e;- if (n >= nbits) {-#ifdef IEEE_Arith /*{*/- switch (rounding) {- case Round_near:- if (n == nbits && (n < 2 || any_on(b,n-1)))- goto ret_tiny;- break;- case Round_up:- if (!sign)- goto ret_tiny;- break;- case Round_down:- if (sign)- goto ret_tiny;- }-#endif /* } IEEE_Arith */- Bfree(C, b);- retz:-#ifndef NO_ERRNO- errno = ERANGE;-#endif- retz1:- rvp->d = 0.;- return;- }- k = n - 1;- if (lostbits)- lostbits = 1;- else if (k > 0)- lostbits = any_on(b,k);- if (x[k>>kshift] & 1 << (k & kmask))- lostbits |= 2;- nbits -= n;- rshift(C, b,n);- e = emin;- }- if (lostbits) {- up = 0;- switch(rounding) {- case Round_zero:- break;- case Round_near:- if (lostbits & 2- && (lostbits & 1) | (x[0] & 1))- up = 1;- break;- case Round_up:- up = 1 - sign;- break;- case Round_down:- up = sign;- }- if (up) {- k = b->wds;- b = increment(C, b);- x = b->x;- if (denorm) {-#if 0- if (nbits == Nbits - 1- && x[nbits >> kshift] & 1 << (nbits & kmask))- denorm = 0; /* not currently used */-#endif- }- else if (b->wds > k- || ((n = nbits & kmask) !=0- && hi0bits(C, x[k-1]) < 32-n)) {- rshift(C, b,1);- if (++e > Emax)- goto ovfl;- }- }- }-#ifdef IEEE_Arith- if (denorm)- word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0;- else- word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20);- word1(rvp) = b->x[0];-#endif-#ifdef IBM- if ((j = e & 3)) {- k = b->x[0] & ((1 << j) - 1);- rshift(C, b,j);- if (k) {- switch(rounding) {- case Round_up:- if (!sign)- increment(b);- break;- case Round_down:- if (sign)- increment(b);- break;- case Round_near:- j = 1 << (j-1);- if (k & j && ((k & (j-1)) | lostbits))- increment(b);- }- }- }- e >>= 2;- word0(rvp) = b->x[1] | ((e + 65 + 13) << 24);- word1(rvp) = b->x[0];-#endif-#ifdef VAX- /* The next two lines ignore swap of low- and high-order 2 bytes. */- /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */- /* word1(rvp) = b->x[0]; */- word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16);- word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16);-#endif- Bfree(C, b);- }-#endif /*!NO_HEX_FP}*/-- static int-dshift(struct dtoa_context* C, Bigint *b, int p2)-{- int rv = hi0bits(C, b->x[b->wds-1]) - 4;- if (p2 > 0)- rv -= p2;- return rv & kmask;- }-- static int-quorem- (struct dtoa_context* C, Bigint *b, Bigint *S)-{- int n;- ULong *bx, *bxe, q, *sx, *sxe;-#ifdef ULLong- ULLong borrow, carry, y, ys;-#else- ULong borrow, carry, y, ys;-#ifdef Pack_32- ULong si, z, zs;-#endif-#endif-- n = S->wds;-#ifdef DEBUG- /*debug*/ if (b->wds > n)- /*debug*/ Bug("oversize b in quorem");-#endif- if (b->wds < n)- return 0;- sx = S->x;- sxe = sx + --n;- bx = b->x;- bxe = bx + n;- q = *bxe / (*sxe + 1); /* ensure q <= true quotient */-#ifdef DEBUG-#ifdef NO_STRTOD_BIGCOMP- /*debug*/ if (q > 9)-#else- /* An oversized q is possible when quorem is called from bigcomp and */- /* the input is near, e.g., twice the smallest denormalized number. */- /*debug*/ if (q > 15)-#endif- /*debug*/ Bug("oversized quotient in quorem");-#endif- if (q) {- borrow = 0;- carry = 0;- do {-#ifdef ULLong- ys = *sx++ * (ULLong)q + carry;- carry = ys >> 32;- y = *bx - (ys & FFFFFFFF) - borrow;- borrow = y >> 32 & (ULong)1;- *bx++ = y & FFFFFFFF;-#else-#ifdef Pack_32- si = *sx++;- ys = (si & 0xffff) * q + carry;- zs = (si >> 16) * q + (ys >> 16);- carry = zs >> 16;- y = (*bx & 0xffff) - (ys & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- z = (*bx >> 16) - (zs & 0xffff) - borrow;- borrow = (z & 0x10000) >> 16;- Storeinc(bx, z, y);-#else- ys = *sx++ * q + carry;- carry = ys >> 16;- y = *bx - (ys & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- *bx++ = y & 0xffff;-#endif-#endif- }- while(sx <= sxe);- if (!*bxe) {- bx = b->x;- while(--bxe > bx && !*bxe)- --n;- b->wds = n;- }- }- if (cmp(C, b, S) >= 0) {- q++;- borrow = 0;- carry = 0;- bx = b->x;- sx = S->x;- do {-#ifdef ULLong- ys = *sx++ + carry;- carry = ys >> 32;- y = *bx - (ys & FFFFFFFF) - borrow;- borrow = y >> 32 & (ULong)1;- *bx++ = y & FFFFFFFF;-#else-#ifdef Pack_32- si = *sx++;- ys = (si & 0xffff) + carry;- zs = (si >> 16) + (ys >> 16);- carry = zs >> 16;- y = (*bx & 0xffff) - (ys & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- z = (*bx >> 16) - (zs & 0xffff) - borrow;- borrow = (z & 0x10000) >> 16;- Storeinc(bx, z, y);-#else- ys = *sx++ + carry;- carry = ys >> 16;- y = *bx - (ys & 0xffff) - borrow;- borrow = (y & 0x10000) >> 16;- *bx++ = y & 0xffff;-#endif-#endif- }- while(sx <= sxe);- bx = b->x;- bxe = bx + n;- if (!*bxe) {- while(--bxe > bx && !*bxe)- --n;- b->wds = n;- }- }- return q;- }--#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/- static double-sulp- (struct dtoa_context* C, U *x, BCinfo *bc)-{- U u;- double rv;- int i;-- rv = ulp(C, x);- if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0)- return rv; /* Is there an example where i <= 0 ? */- word0(&u) = Exp_1 + (i << Exp_shift);- word1(&u) = 0;- return rv * u.d;- }-#endif /*}*/--#ifndef NO_STRTOD_BIGCOMP- static void-bigcomp- (struct dtoa_context* C, U *rv, const char *s0, BCinfo *bc)-{- Bigint *b, *d;- int b2, bbits, d2, dd=0, dig, dsign, i, j, nd, nd0, p2, p5, speccase;-- dsign = bc->dsign;- nd = bc->nd;- nd0 = bc->nd0;- p5 = nd + bc->e0 - 1;- speccase = 0;-#ifndef Sudden_Underflow- if (rv->d == 0.) { /* special case: value near underflow-to-zero */- /* threshold was rounded to zero */- b = i2b(C, 1);- p2 = Emin - P + 1;- bbits = 1;-#ifdef Avoid_Underflow- word0(rv) = (P+2) << Exp_shift;-#else- word1(rv) = 1;-#endif- i = 0;-#ifdef Honor_FLT_ROUNDS- if (bc->rounding == 1)-#endif- {- speccase = 1;- --p2;- dsign = 0;- goto have_i;- }- }- else-#endif- b = d2b(C, rv, &p2, &bbits);-#ifdef Avoid_Underflow- p2 -= bc->scale;-#endif- /* floor(log2(rv)) == bbits - 1 + p2 */- /* Check for denormal case. */- i = P - bbits;- if (i > (j = P - Emin - 1 + p2)) {-#ifdef Sudden_Underflow- Bfree(C, b);- b = i2b(C, 1);- p2 = Emin;- i = P - 1;-#ifdef Avoid_Underflow- word0(rv) = (1 + bc->scale) << Exp_shift;-#else- word0(rv) = Exp_msk1;-#endif- word1(rv) = 0;-#else- i = j;-#endif- }-#ifdef Honor_FLT_ROUNDS- if (bc->rounding != 1) {- if (i > 0)- b = lshift(C, b, i);- if (dsign)- b = increment(b);- }- else-#endif- {- b = lshift(C, b, ++i);- b->x[0] |= 1;- }-#ifndef Sudden_Underflow- have_i:-#endif- p2 -= p5 + i;- d = i2b(C, 1);- /* Arrange for convenient computation of quotients:- * shift left if necessary so divisor has 4 leading 0 bits.- */- if (p5 > 0)- d = pow5mult(C, d, p5);- else if (p5 < 0)- b = pow5mult(C, b, -p5);- if (p2 > 0) {- b2 = p2;- d2 = 0;- }- else {- b2 = 0;- d2 = -p2;- }- i = dshift(C, d, d2);- if ((b2 += i) > 0)- b = lshift(C, b, b2);- if ((d2 += i) > 0)- d = lshift(C, d, d2);-- /* Now b/d = exactly half-way between the two floating-point values */- /* on either side of the input string. Compute first digit of b/d. */-- if (!(dig = quorem(C, b,d))) {- b = multadd(C, b, 10, 0); /* very unlikely */- dig = quorem(C, b,d);- }-- /* Compare b/d with s0 */-- for(i = 0; i < nd0; ) {- if ((dd = s0[i++] - '0' - dig))- goto ret;- if (!b->x[0] && b->wds == 1) {- if (i < nd)- dd = 1;- goto ret;- }- b = multadd(C, b, 10, 0);- dig = quorem(C, b,d);- }- for(j = bc->dp1; i++ < nd;) {- if ((dd = s0[j++] - '0' - dig))- goto ret;- if (!b->x[0] && b->wds == 1) {- if (i < nd)- dd = 1;- goto ret;- }- b = multadd(C, b, 10, 0);- dig = quorem(C, b,d);- }- if (dig > 0 || b->x[0] || b->wds > 1)- dd = -1;- ret:- Bfree(C, b);- Bfree(C, d);-#ifdef Honor_FLT_ROUNDS- if (bc->rounding != 1) {- if (dd < 0) {- if (bc->rounding == 0) {- if (!dsign)- goto retlow1;- }- else if (dsign)- goto rethi1;- }- else if (dd > 0) {- if (bc->rounding == 0) {- if (dsign)- goto rethi1;- goto ret1;- }- if (!dsign)- goto rethi1;- dval(rv) += 2.*sulp(C, rv,bc);- }- else {- bc->inexact = 0;- if (dsign)- goto rethi1;- }- }- else-#endif- if (speccase) {- if (dd <= 0)- rv->d = 0.;- }- else if (dd < 0) {- if (!dsign) /* does not happen for round-near */-retlow1:- dval(rv) -= sulp(C, rv,bc);- }- else if (dd > 0) {- if (dsign) {- rethi1:- dval(rv) += sulp(C, rv,bc);- }- }- else {- /* Exact half-way case: apply round-even rule. */- if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) {- i = 1 - j;- if (i <= 31) {- if (word1(rv) & (0x1 << i))- goto odd;- }- else if (word0(rv) & (0x1 << (i-32)))- goto odd;- }- else if (word1(rv) & 1) {- odd:- if (dsign)- goto rethi1;- goto retlow1;- }- }--#ifdef Honor_FLT_ROUNDS- ret1:-#endif- return;- }-#endif /* NO_STRTOD_BIGCOMP */-- double-jvp_strtod- (struct dtoa_context* C, const char *s00, char **se)-{- int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1;- int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign;- CONST char *s, *s0, *s1;- double aadj, aadj1;- Long L;- U aadj2, adj, rv, rv0;- ULong y, z;- BCinfo bc;- Bigint *bb=0, *bb1, *bd=0, *bd0, *bs=0, *delta=0;-#ifdef Avoid_Underflow- ULong Lsb, Lsb1;-#endif-#ifdef SET_INEXACT- int oldinexact;-#endif-#ifndef NO_STRTOD_BIGCOMP- int req_bigcomp = 0;-#endif-#ifdef Honor_FLT_ROUNDS /*{*/-#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */- bc.rounding = Flt_Rounds;-#else /*}{*/- bc.rounding = 1;- switch(fegetround()) {- case FE_TOWARDZERO: bc.rounding = 0; break;- case FE_UPWARD: bc.rounding = 2; break;- case FE_DOWNWARD: bc.rounding = 3;- }-#endif /*}}*/-#endif /*}*/-#ifdef USE_LOCALE- CONST char *s2;-#endif-- sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0;- dval(&rv) = 0.;- for(s = s00;;s++) switch(*s) {- case '-':- sign = 1;- /* no break */- case '+':- if (*++s)- goto break2;- /* no break */- case 0:- goto ret0;- case '\t':- case '\n':- case '\v':- case '\f':- case '\r':- case ' ':- continue;- default:- goto break2;- }- break2:- if (*s == '0') {-#ifndef NO_HEX_FP /*{*/- switch(s[1]) {- case 'x':- case 'X':-#ifdef Honor_FLT_ROUNDS- gethex(C, &s, &rv, bc.rounding, sign);-#else- gethex(C, &s, &rv, 1, sign);-#endif- goto ret;- }-#endif /*}*/- nz0 = 1;- while(*++s == '0') ;- if (!*s)- goto ret;- }- s0 = s;- y = z = 0;- for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)- if (nd < 9)- y = 10*y + c - '0';- else if (nd < 16)- z = 10*z + c - '0';- nd0 = nd;- bc.dp0 = bc.dp1 = s - s0;- for(s1 = s; s1 > s0 && *--s1 == '0'; )- ++nz1;-#ifdef USE_LOCALE- s1 = localeconv()->decimal_point;- if (c == *s1) {- c = '.';- if (*++s1) {- s2 = s;- for(;;) {- if (*++s2 != *s1) {- c = 0;- break;- }- if (!*++s1) {- s = s2;- break;- }- }- }- }-#endif- if (c == '.') {- c = *++s;- bc.dp1 = s - s0;- bc.dplen = bc.dp1 - bc.dp0;- if (!nd) {- for(; c == '0'; c = *++s)- nz++;- if (c > '0' && c <= '9') {- bc.dp0 = s0 - s;- bc.dp1 = bc.dp0 + bc.dplen;- s0 = s;- nf += nz;- nz = 0;- goto have_dig;- }- goto dig_done;- }- for(; c >= '0' && c <= '9'; c = *++s) {- have_dig:- nz++;- if (c -= '0') {- nf += nz;- for(i = 1; i < nz; i++)- if (nd++ < 9)- y *= 10;- else if (nd <= DBL_DIG + 1)- z *= 10;- if (nd++ < 9)- y = 10*y + c;- else if (nd <= DBL_DIG + 1)- z = 10*z + c;- nz = nz1 = 0;- }- }- }- dig_done:- e = 0;- if (c == 'e' || c == 'E') {- if (!nd && !nz && !nz0) {- goto ret0;- }- s00 = s;- esign = 0;- switch(c = *++s) {- case '-':- esign = 1;- case '+':- c = *++s;- }- if (c >= '0' && c <= '9') {- while(c == '0')- c = *++s;- if (c > '0' && c <= '9') {- L = c - '0';- s1 = s;- while((c = *++s) >= '0' && c <= '9')- L = 10*L + c - '0';- if (s - s1 > 8 || L > 19999)- /* Avoid confusion from exponents- * so large that e might overflow.- */- e = 19999; /* safe for 16 bit ints */- else- e = (int)L;- if (esign)- e = -e;- }- else- e = 0;- }- else- s = s00;- }- if (!nd) {- if (!nz && !nz0) {-#ifdef INFNAN_CHECK- /* Check for Nan and Infinity */- if (!bc.dplen)- switch(c) {- case 'i':- case 'I':- if (match(C, &s,"nf")) {- --s;- if (!match(C, &s,"inity"))- ++s;- word0(&rv) = 0x7ff00000;- word1(&rv) = 0;- goto ret;- }- break;- case 'n':- case 'N':- if (match(C, &s, "an")) {- word0(&rv) = NAN_WORD0;- word1(&rv) = NAN_WORD1;-#ifndef No_Hex_NaN- if (*s == '(') /*)*/- hexnan(C, &rv, &s);-#endif- goto ret;- }- }-#endif /* INFNAN_CHECK */- ret0:- s = s00;- sign = 0;- }- goto ret;- }- bc.e0 = e1 = e -= nf;-- /* Now we have nd0 digits, starting at s0, followed by a- * decimal point, followed by nd-nd0 digits. The number we're- * after is the integer represented by those digits times- * 10**e */-- if (!nd0)- nd0 = nd;- k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;- dval(&rv) = y;- if (k > 9) {-#ifdef SET_INEXACT- if (k > DBL_DIG)- oldinexact = get_inexact();-#endif- dval(&rv) = tens[k - 9] * dval(&rv) + z;- }- bd0 = 0;- if (nd <= DBL_DIG-#ifndef RND_PRODQUOT-#ifndef Honor_FLT_ROUNDS- && Flt_Rounds == 1-#endif-#endif- ) {- if (!e)- goto ret;-#ifndef ROUND_BIASED_without_Round_Up- if (e > 0) {- if (e <= Ten_pmax) {-#ifdef VAX- goto vax_ovfl_check;-#else-#ifdef Honor_FLT_ROUNDS- /* round correctly FLT_ROUNDS = 2 or 3 */- if (sign) {- rv.d = -rv.d;- sign = 0;- }-#endif- /* rv = */ rounded_product(dval(&rv), tens[e]);- goto ret;-#endif- }- i = DBL_DIG - nd;- if (e <= Ten_pmax + i) {- /* A fancier test would sometimes let us do- * this for larger i values.- */-#ifdef Honor_FLT_ROUNDS- /* round correctly FLT_ROUNDS = 2 or 3 */- if (sign) {- rv.d = -rv.d;- sign = 0;- }-#endif- e -= i;- dval(&rv) *= tens[i];-#ifdef VAX- /* VAX exponent range is so narrow we must- * worry about overflow here...- */- vax_ovfl_check:- word0(&rv) -= P*Exp_msk1;- /* rv = */ rounded_product(dval(&rv), tens[e]);- if ((word0(&rv) & Exp_mask)- > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))- goto ovfl;- word0(&rv) += P*Exp_msk1;-#else- /* rv = */ rounded_product(dval(&rv), tens[e]);-#endif- goto ret;- }- }-#ifndef Inaccurate_Divide- else if (e >= -Ten_pmax) {-#ifdef Honor_FLT_ROUNDS- /* round correctly FLT_ROUNDS = 2 or 3 */- if (sign) {- rv.d = -rv.d;- sign = 0;- }-#endif- /* rv = */ rounded_quotient(dval(&rv), tens[-e]);- goto ret;- }-#endif-#endif /* ROUND_BIASED_without_Round_Up */- }- e1 += nd - k;--#ifdef IEEE_Arith-#ifdef SET_INEXACT- bc.inexact = 1;- if (k <= DBL_DIG)- oldinexact = get_inexact();-#endif-#ifdef Avoid_Underflow- bc.scale = 0;-#endif-#ifdef Honor_FLT_ROUNDS- if (bc.rounding >= 2) {- if (sign)- bc.rounding = bc.rounding == 2 ? 0 : 2;- else- if (bc.rounding != 2)- bc.rounding = 0;- }-#endif-#endif /*IEEE_Arith*/-- /* Get starting approximation = rv * 10**e1 */-- if (e1 > 0) {- if ((i = e1 & 15))- dval(&rv) *= tens[i];- if (e1 &= ~15) {- if (e1 > DBL_MAX_10_EXP) {- ovfl:- /* Can't trust HUGE_VAL */-#ifdef IEEE_Arith-#ifdef Honor_FLT_ROUNDS- switch(bc.rounding) {- case 0: /* toward 0 */- case 3: /* toward -infinity */- word0(&rv) = Big0;- word1(&rv) = Big1;- break;- default:- word0(&rv) = Exp_mask;- word1(&rv) = 0;- }-#else /*Honor_FLT_ROUNDS*/- word0(&rv) = Exp_mask;- word1(&rv) = 0;-#endif /*Honor_FLT_ROUNDS*/-#ifdef SET_INEXACT- /* set overflow bit */- dval(&rv0) = 1e300;- dval(&rv0) *= dval(&rv0);-#endif-#else /*IEEE_Arith*/- word0(&rv) = Big0;- word1(&rv) = Big1;-#endif /*IEEE_Arith*/- range_err:- if (bd0) {- Bfree(C, bb);- Bfree(C, bd);- Bfree(C, bs);- Bfree(C, bd0);- Bfree(C, delta);- }-#ifndef NO_ERRNO- errno = ERANGE;-#endif- goto ret;- }- e1 >>= 4;- for(j = 0; e1 > 1; j++, e1 >>= 1)- if (e1 & 1)- dval(&rv) *= bigtens[j];- /* The last multiplication could overflow. */- word0(&rv) -= P*Exp_msk1;- dval(&rv) *= bigtens[j];- if ((z = word0(&rv) & Exp_mask)- > Exp_msk1*(DBL_MAX_EXP+Bias-P))- goto ovfl;- if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {- /* set to largest number */- /* (Can't trust DBL_MAX) */- word0(&rv) = Big0;- word1(&rv) = Big1;- }- else- word0(&rv) += P*Exp_msk1;- }- }- else if (e1 < 0) {- e1 = -e1;- if ((i = e1 & 15))- dval(&rv) /= tens[i];- if (e1 >>= 4) {- if (e1 >= 1 << n_bigtens)- goto undfl;-#ifdef Avoid_Underflow- if (e1 & Scale_Bit)- bc.scale = 2*P;- for(j = 0; e1 > 0; j++, e1 >>= 1)- if (e1 & 1)- dval(&rv) *= tinytens[j];- if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask)- >> Exp_shift)) > 0) {- /* scaled rv is denormal; clear j low bits */- if (j >= 32) {- if (j > 54)- goto undfl;- word1(&rv) = 0;- if (j >= 53)- word0(&rv) = (P+2)*Exp_msk1;- else- word0(&rv) &= 0xffffffff << (j-32);- }- else- word1(&rv) &= 0xffffffff << j;- }-#else- for(j = 0; e1 > 1; j++, e1 >>= 1)- if (e1 & 1)- dval(&rv) *= tinytens[j];- /* The last multiplication could underflow. */- dval(&rv0) = dval(&rv);- dval(&rv) *= tinytens[j];- if (!dval(&rv)) {- dval(&rv) = 2.*dval(&rv0);- dval(&rv) *= tinytens[j];-#endif- if (!dval(&rv)) {- undfl:- dval(&rv) = 0.;- goto range_err;- }-#ifndef Avoid_Underflow- word0(&rv) = Tiny0;- word1(&rv) = Tiny1;- /* The refinement below will clean- * this approximation up.- */- }-#endif- }- }-- /* Now the hard part -- adjusting rv to the correct value.*/-- /* Put digits into bd: true value = bd * 10^e */-- bc.nd = nd - nz1;-#ifndef NO_STRTOD_BIGCOMP- bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */- /* to silence an erroneous warning about bc.nd0 */- /* possibly not being initialized. */- if (nd > strtod_diglim) {- /* ASSERT(strtod_diglim >= 18); 18 == one more than the */- /* minimum number of decimal digits to distinguish double values */- /* in IEEE arithmetic. */- i = j = 18;- if (i > nd0)- j += bc.dplen;- for(;;) {- if (--j < bc.dp1 && j >= bc.dp0)- j = bc.dp0 - 1;- if (s0[j] != '0')- break;- --i;- }- e += nd - i;- nd = i;- if (nd0 > nd)- nd0 = nd;- if (nd < 9) { /* must recompute y */- y = 0;- for(i = 0; i < nd0; ++i)- y = 10*y + s0[i] - '0';- for(j = bc.dp1; i < nd; ++i)- y = 10*y + s0[j++] - '0';- }- }-#endif- bd0 = s2b(C, s0, nd0, nd, y, bc.dplen);-- for(;;) {- bd = Balloc(C, bd0->k);- Bcopy(bd, bd0);- bb = d2b(C, &rv, &bbe, &bbbits); /* rv = bb * 2^bbe */- bs = i2b(C, 1);-- if (e >= 0) {- bb2 = bb5 = 0;- bd2 = bd5 = e;- }- else {- bb2 = bb5 = -e;- bd2 = bd5 = 0;- }- if (bbe >= 0)- bb2 += bbe;- else- bd2 -= bbe;- bs2 = bb2;-#ifdef Honor_FLT_ROUNDS- if (bc.rounding != 1)- bs2++;-#endif-#ifdef Avoid_Underflow- Lsb = LSB;- Lsb1 = 0;- j = bbe - bc.scale;- i = j + bbbits - 1; /* logb(rv) */- j = P + 1 - bbbits;- if (i < Emin) { /* denormal */- i = Emin - i;- j -= i;- if (i < 32)- Lsb <<= i;- else if (i < 52)- Lsb1 = Lsb << (i-32);- else- Lsb1 = Exp_mask;- }-#else /*Avoid_Underflow*/-#ifdef Sudden_Underflow-#ifdef IBM- j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);-#else- j = P + 1 - bbbits;-#endif-#else /*Sudden_Underflow*/- j = bbe;- i = j + bbbits - 1; /* logb(rv) */- if (i < Emin) /* denormal */- j += P - Emin;- else- j = P + 1 - bbbits;-#endif /*Sudden_Underflow*/-#endif /*Avoid_Underflow*/- bb2 += j;- bd2 += j;-#ifdef Avoid_Underflow- bd2 += bc.scale;-#endif- i = bb2 < bd2 ? bb2 : bd2;- if (i > bs2)- i = bs2;- if (i > 0) {- bb2 -= i;- bd2 -= i;- bs2 -= i;- }- if (bb5 > 0) {- bs = pow5mult(C, bs, bb5);- bb1 = mult(C, bs, bb);- Bfree(C, bb);- bb = bb1;- }- if (bb2 > 0)- bb = lshift(C, bb, bb2);- if (bd5 > 0)- bd = pow5mult(C, bd, bd5);- if (bd2 > 0)- bd = lshift(C, bd, bd2);- if (bs2 > 0)- bs = lshift(C, bs, bs2);- delta = diff(C, bb, bd);- bc.dsign = delta->sign;- delta->sign = 0;- i = cmp(C, delta, bs);-#ifndef NO_STRTOD_BIGCOMP /*{*/- if (bc.nd > nd && i <= 0) {- if (bc.dsign) {- /* Must use bigcomp(C, ). */- req_bigcomp = 1;- break;- }-#ifdef Honor_FLT_ROUNDS- if (bc.rounding != 1) {- if (i < 0) {- req_bigcomp = 1;- break;- }- }- else-#endif- i = -1; /* Discarded digits make delta smaller. */- }-#endif /*}*/-#ifdef Honor_FLT_ROUNDS /*{*/- if (bc.rounding != 1) {- if (i < 0) {- /* Error is less than an ulp */- if (!delta->x[0] && delta->wds <= 1) {- /* exact */-#ifdef SET_INEXACT- bc.inexact = 0;-#endif- break;- }- if (bc.rounding) {- if (bc.dsign) {- adj.d = 1.;- goto apply_adj;- }- }- else if (!bc.dsign) {- adj.d = -1.;- if (!word1(&rv)- && !(word0(&rv) & Frac_mask)) {- y = word0(&rv) & Exp_mask;-#ifdef Avoid_Underflow- if (!bc.scale || y > 2*P*Exp_msk1)-#else- if (y)-#endif- {- delta = lshift(C, delta,Log2P);- if (cmp(C, delta, bs) <= 0)- adj.d = -0.5;- }- }- apply_adj:-#ifdef Avoid_Underflow /*{*/- if (bc.scale && (y = word0(&rv) & Exp_mask)- <= 2*P*Exp_msk1)- word0(&adj) += (2*P+1)*Exp_msk1 - y;-#else-#ifdef Sudden_Underflow- if ((word0(&rv) & Exp_mask) <=- P*Exp_msk1) {- word0(&rv) += P*Exp_msk1;- dval(&rv) += adj.d*ulp(C, dval(&rv));- word0(&rv) -= P*Exp_msk1;- }- else-#endif /*Sudden_Underflow*/-#endif /*Avoid_Underflow}*/- dval(&rv) += adj.d*ulp(C, &rv);- }- break;- }- adj.d = ratio(C, delta, bs);- if (adj.d < 1.)- adj.d = 1.;- if (adj.d <= 0x7ffffffe) {- /* adj = rounding ? ceil(adj) : floor(adj); */- y = adj.d;- if (y != adj.d) {- if (!((bc.rounding>>1) ^ bc.dsign))- y++;- adj.d = y;- }- }-#ifdef Avoid_Underflow /*{*/- if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)- word0(&adj) += (2*P+1)*Exp_msk1 - y;-#else-#ifdef Sudden_Underflow- if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {- word0(&rv) += P*Exp_msk1;- adj.d *= ulp(C, dval(&rv));- if (bc.dsign)- dval(&rv) += adj.d;- else- dval(&rv) -= adj.d;- word0(&rv) -= P*Exp_msk1;- goto cont;- }-#endif /*Sudden_Underflow*/-#endif /*Avoid_Underflow}*/- adj.d *= ulp(C, &rv);- if (bc.dsign) {- if (word0(&rv) == Big0 && word1(&rv) == Big1)- goto ovfl;- dval(&rv) += adj.d;- }- else- dval(&rv) -= adj.d;- goto cont;- }-#endif /*}Honor_FLT_ROUNDS*/-- if (i < 0) {- /* Error is less than half an ulp -- check for- * special case of mantissa a power of two.- */- if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask-#ifdef IEEE_Arith /*{*/-#ifdef Avoid_Underflow- || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1-#else- || (word0(&rv) & Exp_mask) <= Exp_msk1-#endif-#endif /*}*/- ) {-#ifdef SET_INEXACT- if (!delta->x[0] && delta->wds <= 1)- bc.inexact = 0;-#endif- break;- }- if (!delta->x[0] && delta->wds <= 1) {- /* exact result */-#ifdef SET_INEXACT- bc.inexact = 0;-#endif- break;- }- delta = lshift(C, delta,Log2P);- if (cmp(C, delta, bs) > 0)- goto drop_down;- break;- }- if (i == 0) {- /* exactly half-way between */- if (bc.dsign) {- if ((word0(&rv) & Bndry_mask1) == Bndry_mask1- && word1(&rv) == (-#ifdef Avoid_Underflow- (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)- ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :-#endif- 0xffffffff)) {- /*boundary case -- increment exponent*/- if (word0(&rv) == Big0 && word1(&rv) == Big1)- goto ovfl;- word0(&rv) = (word0(&rv) & Exp_mask)- + Exp_msk1-#ifdef IBM- | Exp_msk1 >> 4-#endif- ;- word1(&rv) = 0;-#ifdef Avoid_Underflow- bc.dsign = 0;-#endif- break;- }- }- else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) {- drop_down:- /* boundary case -- decrement exponent */-#ifdef Sudden_Underflow /*{{*/- L = word0(&rv) & Exp_mask;-#ifdef IBM- if (L < Exp_msk1)-#else-#ifdef Avoid_Underflow- if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1))-#else- if (L <= Exp_msk1)-#endif /*Avoid_Underflow*/-#endif /*IBM*/- {- if (bc.nd >nd) {- bc.uflchk = 1;- break;- }- goto undfl;- }- L -= Exp_msk1;-#else /*Sudden_Underflow}{*/-#ifdef Avoid_Underflow- if (bc.scale) {- L = word0(&rv) & Exp_mask;- if (L <= (2*P+1)*Exp_msk1) {- if (L > (P+2)*Exp_msk1)- /* round even ==> */- /* accept rv */- break;- /* rv = smallest denormal */- if (bc.nd >nd) {- bc.uflchk = 1;- break;- }- goto undfl;- }- }-#endif /*Avoid_Underflow*/- L = (word0(&rv) & Exp_mask) - Exp_msk1;-#endif /*Sudden_Underflow}}*/- word0(&rv) = L | Bndry_mask1;- word1(&rv) = 0xffffffff;-#ifdef IBM- goto cont;-#else-#ifndef NO_STRTOD_BIGCOMP- if (bc.nd > nd)- goto cont;-#endif- break;-#endif- }-#ifndef ROUND_BIASED-#ifdef Avoid_Underflow- if (Lsb1) {- if (!(word0(&rv) & Lsb1))- break;- }- else if (!(word1(&rv) & Lsb))- break;-#else- if (!(word1(&rv) & LSB))- break;-#endif-#endif- if (bc.dsign)-#ifdef Avoid_Underflow- dval(&rv) += sulp(C, &rv, &bc);-#else- dval(&rv) += ulp(C, &rv);-#endif-#ifndef ROUND_BIASED- else {-#ifdef Avoid_Underflow- dval(&rv) -= sulp(C, &rv, &bc);-#else- dval(&rv) -= ulp(C, &rv);-#endif-#ifndef Sudden_Underflow- if (!dval(&rv)) {- if (bc.nd >nd) {- bc.uflchk = 1;- break;- }- goto undfl;- }-#endif- }-#ifdef Avoid_Underflow- bc.dsign = 1 - bc.dsign;-#endif-#endif- break;- }- if ((aadj = ratio(C, delta, bs)) <= 2.) {- if (bc.dsign)- aadj = aadj1 = 1.;- else if (word1(&rv) || word0(&rv) & Bndry_mask) {-#ifndef Sudden_Underflow- if (word1(&rv) == Tiny1 && !word0(&rv)) {- if (bc.nd >nd) {- bc.uflchk = 1;- break;- }- goto undfl;- }-#endif- aadj = 1.;- aadj1 = -1.;- }- else {- /* special case -- power of FLT_RADIX to be */- /* rounded down... */-- if (aadj < 2./FLT_RADIX)- aadj = 1./FLT_RADIX;- else- aadj *= 0.5;- aadj1 = -aadj;- }- }- else {- aadj *= 0.5;- aadj1 = bc.dsign ? aadj : -aadj;-#ifdef Check_FLT_ROUNDS- switch(bc.rounding) {- case 2: /* towards +infinity */- aadj1 -= 0.5;- break;- case 0: /* towards 0 */- case 3: /* towards -infinity */- aadj1 += 0.5;- }-#else- if (Flt_Rounds == 0)- aadj1 += 0.5;-#endif /*Check_FLT_ROUNDS*/- }- y = word0(&rv) & Exp_mask;-- /* Check for overflow */-- if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {- dval(&rv0) = dval(&rv);- word0(&rv) -= P*Exp_msk1;- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;- if ((word0(&rv) & Exp_mask) >=- Exp_msk1*(DBL_MAX_EXP+Bias-P)) {- if (word0(&rv0) == Big0 && word1(&rv0) == Big1)- goto ovfl;- word0(&rv) = Big0;- word1(&rv) = Big1;- goto cont;- }- else- word0(&rv) += P*Exp_msk1;- }- else {-#ifdef Avoid_Underflow- if (bc.scale && y <= 2*P*Exp_msk1) {- if (aadj <= 0x7fffffff) {- if ((z = aadj) <= 0)- z = 1;- aadj = z;- aadj1 = bc.dsign ? aadj : -aadj;- }- dval(&aadj2) = aadj1;- word0(&aadj2) += (2*P+1)*Exp_msk1 - y;- aadj1 = dval(&aadj2);- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;- if (rv.d == 0.)-#ifdef NO_STRTOD_BIGCOMP- goto undfl;-#else- {- if (bc.nd > nd)- bc.dsign = 1;- break;- }-#endif- }- else {- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;- }-#else-#ifdef Sudden_Underflow- if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {- dval(&rv0) = dval(&rv);- word0(&rv) += P*Exp_msk1;- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;-#ifdef IBM- if ((word0(&rv) & Exp_mask) < P*Exp_msk1)-#else- if ((word0(&rv) & Exp_mask) <= P*Exp_msk1)-#endif- {- if (word0(&rv0) == Tiny0- && word1(&rv0) == Tiny1) {- if (bc.nd >nd) {- bc.uflchk = 1;- break;- }- goto undfl;- }- word0(&rv) = Tiny0;- word1(&rv) = Tiny1;- goto cont;- }- else- word0(&rv) -= P*Exp_msk1;- }- else {- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;- }-#else /*Sudden_Underflow*/- /* Compute adj so that the IEEE rounding rules will- * correctly round rv + adj in some half-way cases.- * If rv * ulp(C, rv) is denormalized (i.e.,- * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid- * trouble from bits lost to denormalization;- * example: 1.2e-307 .- */- if (y <= (P-1)*Exp_msk1 && aadj > 1.) {- aadj1 = (double)(int)(aadj + 0.5);- if (!bc.dsign)- aadj1 = -aadj1;- }- adj.d = aadj1 * ulp(C, &rv);- dval(&rv) += adj.d;-#endif /*Sudden_Underflow*/-#endif /*Avoid_Underflow*/- }- z = word0(&rv) & Exp_mask;-#ifndef SET_INEXACT- if (bc.nd == nd) {-#ifdef Avoid_Underflow- if (!bc.scale)-#endif- if (y == z) {- /* Can we stop now? */- L = (Long)aadj;- aadj -= L;- /* The tolerances below are conservative. */- if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) {- if (aadj < .4999999 || aadj > .5000001)- break;- }- else if (aadj < .4999999/FLT_RADIX)- break;- }- }-#endif- cont:- Bfree(C, bb);- Bfree(C, bd);- Bfree(C, bs);- Bfree(C, delta);- }- Bfree(C, bb);- Bfree(C, bd);- Bfree(C, bs);- Bfree(C, bd0);- Bfree(C, delta);-#ifndef NO_STRTOD_BIGCOMP- if (req_bigcomp) {- bd0 = 0;- bc.e0 += nz1;- bigcomp(C, &rv, s0, &bc);- y = word0(&rv) & Exp_mask;- if (y == Exp_mask)- goto ovfl;- if (y == 0 && rv.d == 0.)- goto undfl;- }-#endif-#ifdef SET_INEXACT- if (bc.inexact) {- if (!oldinexact) {- word0(&rv0) = Exp_1 + (70 << Exp_shift);- word1(&rv0) = 0;- dval(&rv0) += 1.;- }- }- else if (!oldinexact)- clear_inexact();-#endif-#ifdef Avoid_Underflow- if (bc.scale) {- word0(&rv0) = Exp_1 - 2*P*Exp_msk1;- word1(&rv0) = 0;- dval(&rv) *= dval(&rv0);-#ifndef NO_ERRNO- /* try to avoid the bug of testing an 8087 register value */-#ifdef IEEE_Arith- if (!(word0(&rv) & Exp_mask))-#else- if (word0(&rv) == 0 && word1(&rv) == 0)-#endif- errno = ERANGE;-#endif- }-#endif /* Avoid_Underflow */-#ifdef SET_INEXACT- if (bc.inexact && !(word0(&rv) & Exp_mask)) {- /* set underflow bit */- dval(&rv0) = 1e-300;- dval(&rv0) *= dval(&rv0);- }-#endif- ret:- if (se)- *se = (char *)s;- return sign ? -dval(&rv) : dval(&rv);- }-- static char *-rv_alloc(struct dtoa_context* C, int i)-{- int j, k, *r;-- j = sizeof(ULong);- for(k = 0;- (int)(sizeof(Bigint) - sizeof(ULong) - sizeof(int)) + j <= i;- j <<= 1)- k++;- r = (int*)Balloc(C, k);- *r = k;- return- (char *)(r+1);- }-- static char *-nrv_alloc(struct dtoa_context* C, const char *s, char **rve, int n)-{- char *rv, *t;-- t = rv = rv_alloc(C, n);- while((*t = *s++)) t++;- if (rve)- *rve = t;- return rv;- }--/* freedtoa(s) must be used to free values s returned by dtoa- * when MULTIPLE_THREADS is #defined. It should be used in all cases,- * but for consistency with earlier versions of dtoa, it is optional- * when MULTIPLE_THREADS is not defined.- */-- void-jvp_freedtoa(struct dtoa_context* C, char *s)-{- Bigint *b = (Bigint *)((int *)s - 1);- b->maxwds = 1 << (b->k = *(int*)b);- Bfree(C, b);- }--/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.- *- * Inspired by "How to Print Floating-Point Numbers Accurately" by- * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].- *- * Modifications:- * 1. Rather than iterating, we use a simple numeric overestimate- * to determine k = floor(log10(d)). We scale relevant- * quantities using O(log2(k)) rather than O(k) multiplications.- * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't- * try to generate digits strictly left to right. Instead, we- * compute with fewer bits and propagate the carry if necessary- * when rounding the final digit up. This is often faster.- * 3. Under the assumption that input will be rounded nearest,- * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.- * That is, we allow equality in stopping tests when the- * round-nearest rule will give the same floating-point value- * as would satisfaction of the stopping test with strict- * inequality.- * 4. We remove common factors of powers of 2 from relevant- * quantities.- * 5. When converting floating-point integers less than 1e16,- * we use floating-point arithmetic rather than resorting- * to multiple-precision integers.- * 6. When asked to produce fewer than 15 digits, we first try- * to get by with floating-point arithmetic; we resort to- * multiple-precision integer arithmetic only if we cannot- * guarantee that the floating-point calculation has given- * the correctly rounded result. For k requested digits and- * "uniformly" distributed input, the probability is- * something like 10^(k-15) that we must resort to the Long- * calculation.- */-- char *-jvp_dtoa- (struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)-{- /* Arguments ndigits, decpt, sign are similar to those- of ecvt and fcvt; trailing zeros are suppressed from- the returned string. If not null, *rve is set to point- to the end of the return value. If d is +-Infinity or NaN,- then *decpt is set to 9999.-- mode:- 0 ==> shortest string that yields d when read in- and rounded to nearest.- 1 ==> like 0, but with Steele & White stopping rule;- e.g. with IEEE P754 arithmetic , mode 0 gives- 1e23 whereas mode 1 gives 9.999999999999999e22.- 2 ==> max(1,ndigits) significant digits. This gives a- return value similar to that of ecvt, except- that trailing zeros are suppressed.- 3 ==> through ndigits past the decimal point. This- gives a return value similar to that from fcvt,- except that trailing zeros are suppressed, and- ndigits can be negative.- 4,5 ==> similar to 2 and 3, respectively, but (in- round-nearest mode) with the tests of mode 0 to- possibly return a shorter string that rounds to d.- With IEEE arithmetic and compilation with- -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same- as modes 2 and 3 when FLT_ROUNDS != 1.- 6-9 ==> Debugging modes similar to mode - 4: don't try- fast floating-point estimate (if applicable).-- Values of mode other than 0-9 are treated as mode 0.-- Sufficient space is allocated to the return value- to hold the suppressed trailing zeros.- */-- int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,- j, j1=0, k, k0, k_check, leftright, m2, m5, s2, s5,- spec_case, try_quick;- Long L;-#ifndef Sudden_Underflow- int denorm;- ULong x;-#endif- Bigint *b, *b1, *delta, *mlo, *mhi, *S;- U d2, eps, u;- double ds;- char *s, *s0;-#ifndef No_leftright-#ifdef IEEE_Arith- U eps1;-#endif-#endif-#ifdef SET_INEXACT- int inexact, oldinexact;-#endif-#ifdef Honor_FLT_ROUNDS /*{*/- int Rounding;-#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */- Rounding = Flt_Rounds;-#else /*}{*/- Rounding = 1;- switch(fegetround()) {- case FE_TOWARDZERO: Rounding = 0; break;- case FE_UPWARD: Rounding = 2; break;- case FE_DOWNWARD: Rounding = 3;- }-#endif /*}}*/-#endif /*}*/--- u.d = dd;- if (word0(&u) & Sign_bit) {- /* set sign for everything, including 0's and NaNs */- *sign = 1;- word0(&u) &= ~Sign_bit; /* clear sign bit */- }- else- *sign = 0;--#if defined(IEEE_Arith) + defined(VAX)-#ifdef IEEE_Arith- if ((word0(&u) & Exp_mask) == Exp_mask)-#else- if (word0(&u) == 0x8000)-#endif- {- /* Infinity or NaN */- *decpt = 9999;-#ifdef IEEE_Arith- if (!word1(&u) && !(word0(&u) & 0xfffff))- return nrv_alloc(C, "Infinity", rve, 8);-#endif- return nrv_alloc(C, "NaN", rve, 3);- }-#endif-#ifdef IBM- dval(&u) += 0; /* normalize */-#endif- if (!dval(&u)) {- *decpt = 1;- return nrv_alloc(C, "0", rve, 1);- }--#ifdef SET_INEXACT- try_quick = oldinexact = get_inexact();- inexact = 1;-#endif-#ifdef Honor_FLT_ROUNDS- if (Rounding >= 2) {- if (*sign)- Rounding = Rounding == 2 ? 0 : 2;- else- if (Rounding != 2)- Rounding = 0;- }-#endif-- b = d2b(C, &u, &be, &bbits);-#ifdef Sudden_Underflow- i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1));-#else- if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {-#endif- dval(&d2) = dval(&u);- word0(&d2) &= Frac_mask1;- word0(&d2) |= Exp_11;-#ifdef IBM- if (j = 11 - hi0bits(C, word0(&d2) & Frac_mask))- dval(&d2) /= 1 << j;-#endif-- /* log(x) ~=~ log(1.5) + (x-1.5)/1.5- * log10(x) = log(x) / log(10)- * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))- * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)- *- * This suggests computing an approximation k to log10(d) by- *- * k = (i - Bias)*0.301029995663981- * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );- *- * We want k to be too large rather than too small.- * The error in the first-order Taylor series approximation- * is in our favor, so we just round up the constant enough- * to compensate for any error in the multiplication of- * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,- * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,- * adding 1e-13 to the constant term more than suffices.- * Hence we adjust the constant term to 0.1760912590558.- * (We could get a more accurate k by invoking log10,- * but this is probably not worthwhile.)- */-- i -= Bias;-#ifdef IBM- i <<= 2;- i += j;-#endif-#ifndef Sudden_Underflow- denorm = 0;- }- else {- /* d is denormalized */-- i = bbits + be + (Bias + (P-1) - 1);- x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)- : word1(&u) << (32 - i);- dval(&d2) = x;- word0(&d2) -= 31*Exp_msk1; /* adjust exponent */- i -= (Bias + (P-1) - 1) + 1;- denorm = 1;- }-#endif- ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;- k = (int)ds;- if (ds < 0. && ds != k)- k--; /* want k = floor(ds) */- k_check = 1;- if (k >= 0 && k <= Ten_pmax) {- if (dval(&u) < tens[k])- k--;- k_check = 0;- }- j = bbits - i - 1;- if (j >= 0) {- b2 = 0;- s2 = j;- }- else {- b2 = -j;- s2 = 0;- }- if (k >= 0) {- b5 = 0;- s5 = k;- s2 += k;- }- else {- b2 -= k;- b5 = -k;- s5 = 0;- }- if (mode < 0 || mode > 9)- mode = 0;--#ifndef SET_INEXACT-#ifdef Check_FLT_ROUNDS- try_quick = Rounding == 1;-#else- try_quick = 1;-#endif-#endif /*SET_INEXACT*/-- if (mode > 5) {- mode -= 4;- try_quick = 0;- }- leftright = 1;- ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */- /* silence erroneous "gcc -Wall" warning. */- switch(mode) {- case 0:- case 1:- i = 18;- ndigits = 0;- break;- case 2:- leftright = 0;- /* no break */- case 4:- if (ndigits <= 0)- ndigits = 1;- ilim = ilim1 = i = ndigits;- break;- case 3:- leftright = 0;- /* no break */- case 5:- i = ndigits + k + 1;- ilim = i;- ilim1 = i - 1;- if (i <= 0)- i = 1;- }- s = s0 = rv_alloc(C, i);--#ifdef Honor_FLT_ROUNDS- if (mode > 1 && Rounding != 1)- leftright = 0;-#endif-- if (ilim >= 0 && ilim <= Quick_max && try_quick) {-- /* Try to get by with floating-point arithmetic. */-- i = 0;- dval(&d2) = dval(&u);- k0 = k;- ilim0 = ilim;- ieps = 2; /* conservative */- if (k > 0) {- ds = tens[k&0xf];- j = k >> 4;- if (j & Bletch) {- /* prevent overflows */- j &= Bletch - 1;- dval(&u) /= bigtens[n_bigtens-1];- ieps++;- }- for(; j; j >>= 1, i++)- if (j & 1) {- ieps++;- ds *= bigtens[i];- }- dval(&u) /= ds;- }- else if ((j1 = -k)) {- dval(&u) *= tens[j1 & 0xf];- for(j = j1 >> 4; j; j >>= 1, i++)- if (j & 1) {- ieps++;- dval(&u) *= bigtens[i];- }- }- if (k_check && dval(&u) < 1. && ilim > 0) {- if (ilim1 <= 0)- goto fast_failed;- ilim = ilim1;- k--;- dval(&u) *= 10.;- ieps++;- }- dval(&eps) = ieps*dval(&u) + 7.;- word0(&eps) -= (P-1)*Exp_msk1;- if (ilim == 0) {- S = mhi = 0;- dval(&u) -= 5.;- if (dval(&u) > dval(&eps))- goto one_digit;- if (dval(&u) < -dval(&eps))- goto no_digits;- goto fast_failed;- }-#ifndef No_leftright- if (leftright) {- /* Use Steele & White method of only- * generating digits needed.- */- dval(&eps) = 0.5/tens[ilim-1] - dval(&eps);-#ifdef IEEE_Arith- if (k0 < 0 && j1 >= 307) {- eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */- word0(&eps1) -= Exp_msk1 * (Bias+P-1);- dval(&eps1) *= tens[j1 & 0xf];- for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++)- if (j & 1)- dval(&eps1) *= bigtens[i];- if (eps.d < eps1.d)- eps.d = eps1.d;- }-#endif- for(i = 0;;) {- L = dval(&u);- dval(&u) -= L;- *s++ = '0' + (int)L;- if (1. - dval(&u) < dval(&eps))- goto bump_up;- if (dval(&u) < dval(&eps))- goto ret1;- if (++i >= ilim)- break;- dval(&eps) *= 10.;- dval(&u) *= 10.;- }- }- else {-#endif- /* Generate ilim digits, then fix them up. */- dval(&eps) *= tens[ilim-1];- for(i = 1;; i++, dval(&u) *= 10.) {- L = (Long)(dval(&u));- if (!(dval(&u) -= L))- ilim = i;- *s++ = '0' + (int)L;- if (i == ilim) {- if (dval(&u) > 0.5 + dval(&eps))- goto bump_up;- else if (dval(&u) < 0.5 - dval(&eps)) {- while(*--s == '0');- s++;- goto ret1;- }- break;- }- }-#ifndef No_leftright- }-#endif- fast_failed:- s = s0;- dval(&u) = dval(&d2);- k = k0;- ilim = ilim0;- }-- /* Do we have a "small" integer? */-- if (be >= 0 && k <= Int_max) {- /* Yes. */- ds = tens[k];- if (ndigits < 0 && ilim <= 0) {- S = mhi = 0;- if (ilim < 0 || dval(&u) <= 5*ds)- goto no_digits;- goto one_digit;- }- for(i = 1;; i++, dval(&u) *= 10.) {- L = (Long)(dval(&u) / ds);- dval(&u) -= L*ds;-#ifdef Check_FLT_ROUNDS- /* If FLT_ROUNDS == 2, L will usually be high by 1 */- if (dval(&u) < 0) {- L--;- dval(&u) += ds;- }-#endif- *s++ = '0' + (int)L;- if (!dval(&u)) {-#ifdef SET_INEXACT- inexact = 0;-#endif- break;- }- if (i == ilim) {-#ifdef Honor_FLT_ROUNDS- if (mode > 1)- switch(Rounding) {- case 0: goto ret1;- case 2: goto bump_up;- }-#endif- dval(&u) += dval(&u);-#ifdef ROUND_BIASED- if (dval(&u) >= ds)-#else- if (dval(&u) > ds || (dval(&u) == ds && L & 1))-#endif- {- bump_up:- while(*--s == '9')- if (s == s0) {- k++;- *s = '0';- break;- }- ++*s++;- }- break;- }- }- goto ret1;- }-- m2 = b2;- m5 = b5;- mhi = mlo = 0;- if (leftright) {- i =-#ifndef Sudden_Underflow- denorm ? be + (Bias + (P-1) - 1 + 1) :-#endif-#ifdef IBM- 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);-#else- 1 + P - bbits;-#endif- b2 += i;- s2 += i;- mhi = i2b(C, 1);- }- if (m2 > 0 && s2 > 0) {- i = m2 < s2 ? m2 : s2;- b2 -= i;- m2 -= i;- s2 -= i;- }- if (b5 > 0) {- if (leftright) {- if (m5 > 0) {- mhi = pow5mult(C, mhi, m5);- b1 = mult(C, mhi, b);- Bfree(C, b);- b = b1;- }- if ((j = b5 - m5))- b = pow5mult(C, b, j);- }- else- b = pow5mult(C, b, b5);- }- S = i2b(C, 1);- if (s5 > 0)- S = pow5mult(C, S, s5);-- /* Check for special case that d is a normalized power of 2. */-- spec_case = 0;- if ((mode < 2 || leftright)-#ifdef Honor_FLT_ROUNDS- && Rounding == 1-#endif- ) {- if (!word1(&u) && !(word0(&u) & Bndry_mask)-#ifndef Sudden_Underflow- && word0(&u) & (Exp_mask & ~Exp_msk1)-#endif- ) {- /* The special case */- b2 += Log2P;- s2 += Log2P;- spec_case = 1;- }- }-- /* Arrange for convenient computation of quotients:- * shift left if necessary so divisor has 4 leading 0 bits.- *- * Perhaps we should just compute leading 28 bits of S once- * and for all and pass them and a shift to quorem, so it- * can do shifts and ors to compute the numerator for q.- */- i = dshift(C, S, s2);- b2 += i;- m2 += i;- s2 += i;- if (b2 > 0)- b = lshift(C, b, b2);- if (s2 > 0)- S = lshift(C, S, s2);- if (k_check) {- if (cmp(C, b,S) < 0) {- k--;- b = multadd(C, b, 10, 0); /* we botched the k estimate */- if (leftright)- mhi = multadd(C, mhi, 10, 0);- ilim = ilim1;- }- }- if (ilim <= 0 && (mode == 3 || mode == 5)) {- if (ilim < 0 || cmp(C, b,S = multadd(C, S,5,0)) <= 0) {- /* no digits, fcvt style */- no_digits:- k = -1 - ndigits;- goto ret;- }- one_digit:- *s++ = '1';- k++;- goto ret;- }- if (leftright) {- if (m2 > 0)- mhi = lshift(C, mhi, m2);-- /* Compute mlo -- check for special case- * that d is a normalized power of 2.- */-- mlo = mhi;- if (spec_case) {- mhi = Balloc(C, mhi->k);- Bcopy(mhi, mlo);- mhi = lshift(C, mhi, Log2P);- }-- for(i = 1;;i++) {- dig = quorem(C, b,S) + '0';- /* Do we yet have the shortest decimal string- * that will round to d?- */- j = cmp(C, b, mlo);- delta = diff(C, S, mhi);- j1 = delta->sign ? 1 : cmp(C, b, delta);- Bfree(C, delta);-#ifndef ROUND_BIASED- if (j1 == 0 && mode != 1 && !(word1(&u) & 1)-#ifdef Honor_FLT_ROUNDS- && Rounding >= 1-#endif- ) {- if (dig == '9')- goto round_9_up;- if (j > 0)- dig++;-#ifdef SET_INEXACT- else if (!b->x[0] && b->wds <= 1)- inexact = 0;-#endif- *s++ = dig;- goto ret;- }-#endif- if (j < 0 || (j == 0 && mode != 1-#ifndef ROUND_BIASED- && !(word1(&u) & 1)-#endif- )) {- if (!b->x[0] && b->wds <= 1) {-#ifdef SET_INEXACT- inexact = 0;-#endif- goto accept_dig;- }-#ifdef Honor_FLT_ROUNDS- if (mode > 1)- switch(Rounding) {- case 0: goto accept_dig;- case 2: goto keep_dig;- }-#endif /*Honor_FLT_ROUNDS*/- if (j1 > 0) {- b = lshift(C, b, 1);- j1 = cmp(C, b, S);-#ifdef ROUND_BIASED- if (j1 >= 0 /*)*/-#else- if ((j1 > 0 || (j1 == 0 && dig & 1))-#endif- && dig++ == '9')- goto round_9_up;- }- accept_dig:- *s++ = dig;- goto ret;- }- if (j1 > 0) {-#ifdef Honor_FLT_ROUNDS- if (!Rounding)- goto accept_dig;-#endif- if (dig == '9') { /* possible if i == 1 */- round_9_up:- *s++ = '9';- goto roundoff;- }- *s++ = dig + 1;- goto ret;- }-#ifdef Honor_FLT_ROUNDS- keep_dig:-#endif- *s++ = dig;- if (i == ilim)- break;- b = multadd(C, b, 10, 0);- if (mlo == mhi)- mlo = mhi = multadd(C, mhi, 10, 0);- else {- mlo = multadd(C, mlo, 10, 0);- mhi = multadd(C, mhi, 10, 0);- }- }- }- else- for(i = 1;; i++) {- *s++ = dig = quorem(C, b,S) + '0';- if (!b->x[0] && b->wds <= 1) {-#ifdef SET_INEXACT- inexact = 0;-#endif- goto ret;- }- if (i >= ilim)- break;- b = multadd(C, b, 10, 0);- }-- /* Round off last digit */--#ifdef Honor_FLT_ROUNDS- switch(Rounding) {- case 0: goto trimzeros;- case 2: goto roundoff;- }-#endif- b = lshift(C, b, 1);- j = cmp(C, b, S);-#ifdef ROUND_BIASED- if (j >= 0)-#else- if (j > 0 || (j == 0 && dig & 1))-#endif- {- roundoff:- while(*--s == '9')- if (s == s0) {- k++;- *s++ = '1';- goto ret;- }- ++*s++;- }- else {-#ifdef Honor_FLT_ROUNDS- trimzeros:-#endif- while(*--s == '0');- s++;- }- ret:- Bfree(C, S);- if (mhi) {- if (mlo && mlo != mhi)- Bfree(C, mlo);- Bfree(C, mhi);- }- ret1:-#ifdef SET_INEXACT- if (inexact) {- if (!oldinexact) {- word0(&u) = Exp_1 + (70 << Exp_shift);- word1(&u) = 0;- dval(&u) += 1.;- }- }- else if (!oldinexact)- clear_inexact();-#endif- Bfree(C, b);- *s = 0;- *decpt = k + 1;- if (rve)- *rve = s;- return s0;- }-#ifdef __cplusplus-}-#endif---------/****************************************************************- *- * The author of this software is David M. Gay.- *- * Copyright (c) 1991, 1996 by Lucent Technologies.- *- * Permission to use, copy, modify, and distribute this software for any- * purpose without fee is hereby granted, provided that this entire notice- * is included in all copies of any software which is or includes a copy- * or modification of this software and in all copies of the supporting- * documentation for such software.- *- * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED- * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY- * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY- * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.- *- ***************************************************************/--/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;- * it suffices to declare buf- * char buf[32];- */-- char *- jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x)-{- register int i, k;- register char *s;- int decpt, j, sign;- char *b0, *s0, *se;-- b0 = b;-#ifdef IGNORE_ZERO_SIGN- if (!x) {- *b++ = '0';- *b = 0;- goto done;- }-#endif- s = s0 = jvp_dtoa(C, x, 0, 0, &decpt, &sign, &se);- if (sign)- *b++ = '-';- if (decpt == 9999) /* Infinity or Nan */ {- while((*b++ = *s++));- goto done0;- }- if (decpt <= -4 || decpt > se - s + 15) {- *b++ = *s++;- if (*s) {- *b++ = '.';- while((*b = *s++))- b++;- }- *b++ = 'e';- /* sprintf(b, "%+.2d", decpt - 1); */- if (--decpt < 0) {- *b++ = '-';- decpt = -decpt;- }- else- *b++ = '+';- for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);- for(;;) {- i = decpt / k;- *b++ = i + '0';- if (--j <= 0)- break;- decpt -= i*k;- decpt *= 10;- }- *b = 0;- }- else if (decpt <= 0) {- *b++ = '0';- *b++ = '.';- for(; decpt < 0; decpt++)- *b++ = '0';- while((*b++ = *s++));- }- else {- while((*b = *s++)) {- b++;- if (--decpt == 0 && *s)- *b++ = '.';- }- for(; decpt > 0; decpt--)- *b++ = '0';- *b = 0;- }- done0:- jvp_freedtoa(C, s0);- goto done;- done:- return b0;- }
jv_dtoa.h
@@ -1,22 +0,0 @@-#ifndef JV_DTOA_H-#define JV_DTOA_H-#define Kmax 7--struct Bigint;-struct dtoa_context {- struct Bigint *freelist[Kmax+1];- struct Bigint *p5s;-};--void jvp_dtoa_context_init(struct dtoa_context* ctx);-void jvp_dtoa_context_free(struct dtoa_context* ctx);--double jvp_strtod(struct dtoa_context* C, const char* s, char** se);---char* jvp_dtoa(struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve);-void jvp_freedtoa(struct dtoa_context* C, char *s);--#define JVP_DTOA_FMT_MAX_LEN 64-char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);-#endif
jv_file.c
@@ -1,49 +0,0 @@--#include <errno.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include "jv.h"--jv jv_load_file(const char* filename, int raw) {- FILE* file = fopen(filename, "r");- struct jv_parser* parser;- jv data;- if (!file) {- return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",- filename,- strerror(errno)));- }- if (raw) {- data = jv_string("");- } else {- data = jv_array();- parser = jv_parser_new(0);- }- while (!feof(file) && !ferror(file)) {- char buf[4096];- size_t n = fread(buf, 1, sizeof(buf), file);- if (raw) {- data = jv_string_concat(data, jv_string_sized(buf, (int)n));- } else {- jv_parser_set_buf(parser, buf, n, !feof(file));- jv value;- while (jv_is_valid((value = jv_parser_next(parser))))- data = jv_array_append(data, value);- if (jv_invalid_has_msg(jv_copy(value))) {- jv_free(data);- data = value;- break;- }- }- }- if (!raw)- jv_parser_free(parser);- int badread = ferror(file);- if (fclose(file) != 0 || badread) {- jv_free(data);- return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",- filename));- }- return data;-}
jv_parse.c
@@ -1,852 +0,0 @@-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include <assert.h>-#include "jv.h"-#include "jv_dtoa.h"-#include "jv_unicode.h"-#include "jv_alloc.h"-#include "jv_dtoa.h"--typedef const char* presult;--#define TRY(x) do {presult msg__ = (x); if (msg__) return msg__; } while(0)-#ifdef __GNUC__-#define pfunc __attribute__((warn_unused_result)) presult-#else-#define pfunc presult-#endif--enum last_seen {- JV_LAST_NONE = 0,- JV_LAST_OPEN_ARRAY = '[',- JV_LAST_OPEN_OBJECT = '{',- JV_LAST_COLON = ':',- JV_LAST_COMMA = ',',- JV_LAST_VALUE = 'V',-};--struct jv_parser {- const char* curr_buf;- int curr_buf_length;- int curr_buf_pos;- int curr_buf_is_partial;- int eof;- unsigned bom_strip_position;-- int flags;-- jv* stack; // parser- int stackpos; // parser- int stacklen; // both (optimization; it's really pathlen for streaming)- jv path; // streamer- enum last_seen last_seen; // streamer- jv output; // streamer- jv next; // both-- char* tokenbuf;- int tokenpos;- int tokenlen;-- int line, column;-- struct dtoa_context dtoa;-- enum {- JV_PARSER_NORMAL,- JV_PARSER_STRING,- JV_PARSER_STRING_ESCAPE,- JV_PARSER_WAITING_FOR_RS // parse error, waiting for RS- } st;- unsigned int last_ch_was_ws:1;-};---static void parser_init(struct jv_parser* p, int flags) {- p->flags = flags;- if ((p->flags & JV_PARSE_STREAMING)) {- p->path = jv_array();- } else {- p->path = jv_invalid();- p->flags &= ~(JV_PARSE_STREAM_ERRORS);- }- p->stack = 0;- p->stacklen = p->stackpos = 0;- p->last_seen = JV_LAST_NONE;- p->output = jv_invalid();- p->next = jv_invalid();- p->tokenbuf = 0;- p->tokenlen = p->tokenpos = 0;- if ((p->flags & JV_PARSE_SEQ))- p->st = JV_PARSER_WAITING_FOR_RS;- else- p->st = JV_PARSER_NORMAL;- p->eof = 0;- p->curr_buf = 0;- p->curr_buf_length = p->curr_buf_pos = p->curr_buf_is_partial = 0;- p->bom_strip_position = 0;- p->last_ch_was_ws = 0;- p->line = 1;- p->column = 0;- jvp_dtoa_context_init(&p->dtoa);-}--static void parser_reset(struct jv_parser* p) {- if ((p->flags & JV_PARSE_STREAMING)) {- jv_free(p->path);- p->path = jv_array();- p->stacklen = 0;- }- p->last_seen = JV_LAST_NONE;- jv_free(p->output);- p->output = jv_invalid();- jv_free(p->next);- p->next = jv_invalid();- for (int i=0; i<p->stackpos; i++)- jv_free(p->stack[i]);- p->stackpos = 0;- p->tokenpos = 0;- p->st = JV_PARSER_NORMAL;-}--static void parser_free(struct jv_parser* p) {- parser_reset(p);- jv_free(p->path);- jv_free(p->output);- jv_mem_free(p->stack);- jv_mem_free(p->tokenbuf);- jvp_dtoa_context_free(&p->dtoa);-}--static pfunc value(struct jv_parser* p, jv val) {- if ((p->flags & JV_PARSE_STREAMING)) {- if (jv_is_valid(p->next) || p->last_seen == JV_LAST_VALUE)- return "Expected separator between values";- if (p->stacklen > 0)- p->last_seen = JV_LAST_VALUE;- else- p->last_seen = JV_LAST_NONE;- } else {- if (jv_is_valid(p->next)) return "Expected separator between values";- }- jv_free(p->next);- p->next = val;- return 0;-}--static void push(struct jv_parser* p, jv v) {- assert(p->stackpos <= p->stacklen);- if (p->stackpos == p->stacklen) {- p->stacklen = p->stacklen * 2 + 10;- p->stack = jv_mem_realloc(p->stack, p->stacklen * sizeof(jv));- }- assert(p->stackpos < p->stacklen);- p->stack[p->stackpos++] = v;-}--static pfunc parse_token(struct jv_parser* p, char ch) {- switch (ch) {- case '[':- if (jv_is_valid(p->next)) return "Expected separator between values";- push(p, jv_array());- break;-- case '{':- if (jv_is_valid(p->next)) return "Expected separator between values";- push(p, jv_object());- break;-- case ':':- if (!jv_is_valid(p->next))- return "Expected string key before ':'";- if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)- return "':' not as part of an object";- if (jv_get_kind(p->next) != JV_KIND_STRING)- return "Object keys must be strings";- push(p, p->next);- p->next = jv_invalid();- break;-- case ',':- if (!jv_is_valid(p->next))- return "Expected value before ','";- if (p->stackpos == 0)- return "',' not as part of an object or array";- if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_ARRAY) {- p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);- p->next = jv_invalid();- } else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {- assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);- p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],- p->stack[p->stackpos-1], p->next);- p->stackpos--;- p->next = jv_invalid();- } else {- // this case hits on input like {"a", "b"}- return "Objects must consist of key:value pairs";- }- break;-- case ']':- if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)- return "Unmatched ']'";- if (jv_is_valid(p->next)) {- p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);- p->next = jv_invalid();- } else {- if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {- // this case hits on input like [1,2,3,]- return "Expected another array element";- }- }- jv_free(p->next);- p->next = p->stack[--p->stackpos];- break;-- case '}':- if (p->stackpos == 0)- return "Unmatched '}'";- if (jv_is_valid(p->next)) {- if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)- return "Objects must consist of key:value pairs";- assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);- p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],- p->stack[p->stackpos-1], p->next);- p->stackpos--;- p->next = jv_invalid();- } else {- if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)- return "Unmatched '}'";- if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)- return "Expected another key-value pair";- }- jv_free(p->next);- p->next = p->stack[--p->stackpos];- break;- }- return 0;-}--static pfunc stream_token(struct jv_parser* p, char ch) {- jv_kind k;- jv last;-- switch (ch) {- case '[':- if (jv_is_valid(p->next))- return "Expected a separator between values";- p->path = jv_array_append(p->path, jv_number(0)); // push- p->last_seen = JV_LAST_OPEN_ARRAY;- p->stacklen++;- break;-- case '{':- if (p->last_seen == JV_LAST_VALUE)- return "Expected a separator between values";- // Push object key: null, since we don't know it yet- p->path = jv_array_append(p->path, jv_null()); // push- p->last_seen = JV_LAST_OPEN_OBJECT;- p->stacklen++;- break;-- case ':':- if (p->stacklen == 0 || jv_get_kind(jv_array_get(jv_copy(p->path), p->stacklen - 1)) == JV_KIND_NUMBER)- return "':' not as part of an object";- if (!jv_is_valid(p->next) || p->last_seen == JV_LAST_NONE)- return "Expected string key before ':'";- if (jv_get_kind(p->next) != JV_KIND_STRING)- return "Object keys must be strings";- if (p->last_seen != JV_LAST_VALUE)- return "':' should follow a key";- p->last_seen = JV_LAST_COLON;- p->path = jv_array_set(p->path, p->stacklen - 1, p->next);- p->next = jv_invalid();- break;-- case ',':- if (p->last_seen != JV_LAST_VALUE)- return "Expected value before ','";- if (p->stacklen == 0)- return "',' not as part of an object or array";- last = jv_array_get(jv_copy(p->path), p->stacklen - 1);- k = jv_get_kind(last);- if (k == JV_KIND_NUMBER) {- int idx = jv_number_value(last);-- if (jv_is_valid(p->next)) {- p->output = JV_ARRAY(jv_copy(p->path), p->next);- p->next = jv_invalid();- }- p->path = jv_array_set(p->path, p->stacklen - 1, jv_number(idx + 1));- p->last_seen = JV_LAST_COMMA;- } else if (k == JV_KIND_STRING) {- if (jv_is_valid(p->next)) {- p->output = JV_ARRAY(jv_copy(p->path), p->next);- p->next = jv_invalid();- }- p->path = jv_array_set(p->path, p->stacklen - 1, jv_true()); // ready for another name:value pair- p->last_seen = JV_LAST_COMMA;- } else {- assert(k == JV_KIND_NULL);- // this case hits on input like {,}- // make sure to handle input like {"a", "b"} and {"a":, ...}- jv_free(last);- return "Objects must consist of key:value pairs";- }- jv_free(last);- break;-- case ']':- if (p->stacklen == 0)- return "Unmatched ']' at the top-level";- if (p->last_seen == JV_LAST_COMMA)- return "Expected another array element";- if (p->last_seen == JV_LAST_OPEN_ARRAY)- assert(!jv_is_valid(p->next));-- last = jv_array_get(jv_copy(p->path), p->stacklen - 1);- k = jv_get_kind(last);- jv_free(last);-- if (k != JV_KIND_NUMBER)- return "Unmatched ']' in the middle of an object";- if (jv_is_valid(p->next)) {- p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());- p->next = jv_invalid();- } else if (p->last_seen != JV_LAST_OPEN_ARRAY) {- p->output = JV_ARRAY(jv_copy(p->path));- }-- p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop- //assert(!jv_is_valid(p->next));- jv_free(p->next);- p->next = jv_invalid();-- if (p->last_seen == JV_LAST_OPEN_ARRAY)- p->output = JV_ARRAY(jv_copy(p->path), jv_array()); // Empty arrays are leaves-- if (p->stacklen == 0)- p->last_seen = JV_LAST_NONE;- else- p->last_seen = JV_LAST_VALUE;- break;-- case '}':- if (p->stacklen == 0)- return "Unmatched '}' at the top-level";- if (p->last_seen == JV_LAST_COMMA)- return "Expected another key:value pair";- if (p->last_seen == JV_LAST_OPEN_OBJECT)- assert(!jv_is_valid(p->next));-- last = jv_array_get(jv_copy(p->path), p->stacklen - 1);- k = jv_get_kind(last);- jv_free(last);- if (k == JV_KIND_NUMBER)- return "Unmatched '}' in the middle of an array";-- if (jv_is_valid(p->next)) {- if (k != JV_KIND_STRING)- return "Objects must consist of key:value pairs";- p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());- p->next = jv_invalid();- } else {- // Perhaps {"a":[]}- if (p->last_seen == JV_LAST_COLON)- // Looks like {"a":}- return "Missing value in key:value pair";- if (p->last_seen == JV_LAST_COMMA)- // Looks like {"a":0,}- return "Expected another key-value pair";- if (p->last_seen == JV_LAST_OPEN_ARRAY)- return "Unmatched '}' in the middle of an array";- if (p->last_seen != JV_LAST_VALUE && p->last_seen != JV_LAST_OPEN_OBJECT)- return "Unmatched '}'";- if (p->last_seen != JV_LAST_OPEN_OBJECT)- p->output = JV_ARRAY(jv_copy(p->path));- }- p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop- jv_free(p->next);- p->next = jv_invalid();-- if (p->last_seen == JV_LAST_OPEN_OBJECT)- p->output = JV_ARRAY(jv_copy(p->path), jv_object()); // Empty arrays are leaves-- if (p->stacklen == 0)- p->last_seen = JV_LAST_NONE;- else- p->last_seen = JV_LAST_VALUE;- break;- }- return 0;-}--static void tokenadd(struct jv_parser* p, char c) {- assert(p->tokenpos <= p->tokenlen);- if (p->tokenpos == p->tokenlen) {- p->tokenlen = p->tokenlen*2 + 256;- p->tokenbuf = jv_mem_realloc(p->tokenbuf, p->tokenlen);- }- assert(p->tokenpos < p->tokenlen);- p->tokenbuf[p->tokenpos++] = c;-}--static int unhex4(char* hex) {- int r = 0;- for (int i=0; i<4; i++) {- char c = *hex++;- int n;- if ('0' <= c && c <= '9') n = c - '0';- else if ('a' <= c && c <= 'f') n = c - 'a' + 10;- else if ('A' <= c && c <= 'F') n = c - 'A' + 10;- else return -1;- r <<= 4;- r |= n;- }- return r;-}--static pfunc found_string(struct jv_parser* p) {- char* in = p->tokenbuf;- char* out = p->tokenbuf;- char* end = p->tokenbuf + p->tokenpos;-- while (in < end) {- char c = *in++;- if (c == '\\') {- if (in >= end)- return "Expected escape character at end of string";- c = *in++;- switch (c) {- case '\\':- case '"':- case '/': *out++ = c; break;- case 'b': *out++ = '\b'; break;- case 'f': *out++ = '\f'; break;- case 't': *out++ = '\t'; break;- case 'n': *out++ = '\n'; break;- case 'r': *out++ = '\r'; break;-- case 'u':- /* ahh, the complicated case */- if (in + 4 > end)- return "Invalid \\uXXXX escape";- int hexvalue = unhex4(in);- if (hexvalue < 0)- return "Invalid characters in \\uXXXX escape";- unsigned long codepoint = (unsigned long)hexvalue;- in += 4;- if (0xD800 <= codepoint && codepoint <= 0xDBFF) {- /* who thought UTF-16 surrogate pairs were a good idea? */- if (in + 6 > end || in[0] != '\\' || in[1] != 'u')- return "Invalid \\uXXXX\\uXXXX surrogate pair escape";- unsigned long surrogate = unhex4(in+2);- if (!(0xDC00 <= surrogate && surrogate <= 0xDFFF))- return "Invalid \\uXXXX\\uXXXX surrogate pair escape";- in += 6;- codepoint = 0x10000 + (((codepoint - 0xD800) << 10)- |(surrogate - 0xDC00));- }- if (codepoint > 0x10FFFF)- codepoint = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER- out += jvp_utf8_encode(codepoint, out);- break;-- default:- return "Invalid escape";- }- } else {- if (c > 0 && c < 0x001f)- return "Invalid string: control characters from U+0000 through U+001F must be escaped";- *out++ = c;- }- }- TRY(value(p, jv_string_sized(p->tokenbuf, out - p->tokenbuf)));- p->tokenpos = 0;- return 0;-}--static pfunc check_literal(struct jv_parser* p) {- if (p->tokenpos == 0) return 0;-- const char* pattern = 0;- int plen;- jv v;- switch (p->tokenbuf[0]) {- case 't': pattern = "true"; plen = 4; v = jv_true(); break;- case 'f': pattern = "false"; plen = 5; v = jv_false(); break;- case 'n': pattern = "null"; plen = 4; v = jv_null(); break;- }- if (pattern) {- if (p->tokenpos != plen) return "Invalid literal";- for (int i=0; i<plen; i++)- if (p->tokenbuf[i] != pattern[i])- return "Invalid literal";- TRY(value(p, v));- } else {- // FIXME: better parser- p->tokenbuf[p->tokenpos] = 0; // FIXME: invalid- char* end = 0;- double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end);- if (end == 0 || *end != 0)- return "Invalid numeric literal";- TRY(value(p, jv_number(d)));- }- p->tokenpos = 0;- return 0;-}--typedef enum {- LITERAL,- WHITESPACE,- STRUCTURE,- QUOTE,- INVALID-} chclass;--static chclass classify(char c) {- switch (c) {- case ' ':- case '\t':- case '\r':- case '\n':- return WHITESPACE;- case '"':- return QUOTE;- case '[':- case ',':- case ']':- case '{':- case ':':- case '}':- return STRUCTURE;- default:- return LITERAL;- }-}---static const presult OK = "output produced";--static int parse_check_done(struct jv_parser* p, jv* out) {- if (p->stackpos == 0 && jv_is_valid(p->next)) {- *out = p->next;- p->next = jv_invalid();- return 1;- } else {- return 0;- }-}--static int stream_check_done(struct jv_parser* p, jv* out) {- if (p->stacklen == 0 && jv_is_valid(p->next)) {- *out = JV_ARRAY(jv_copy(p->path),p->next);- p->next = jv_invalid();- return 1;- } else if (jv_is_valid(p->output)) {- if (jv_array_length(jv_copy(p->output)) > 2) {- // At end of an array or object, necessitating one more output by- // which to indicate this- *out = jv_array_slice(jv_copy(p->output), 0, 2);- p->output = jv_array_slice(p->output, 0, 1); // arrange one more output- } else {- // No further processing needed- *out = p->output;- p->output = jv_invalid();- }- return 1;- } else {- return 0;- }-}--static int parse_check_truncation(struct jv_parser* p) {- return ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && (p->stackpos > 0 || p->tokenpos > 0 || jv_get_kind(p->next) == JV_KIND_NUMBER));-}--static int stream_check_truncation(struct jv_parser* p) {- jv_kind k = jv_get_kind(p->next);- return (p->stacklen > 0 || k == JV_KIND_NUMBER || k == JV_KIND_TRUE || k == JV_KIND_FALSE || k == JV_KIND_NULL);-}--static int parse_is_top_num(struct jv_parser* p) {- return (p->stackpos == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);-}--static int stream_is_top_num(struct jv_parser* p) {- return (p->stacklen == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);-}--#define check_done(p, o) \- (((p)->flags & JV_PARSE_STREAMING) ? stream_check_done((p), (o)) : parse_check_done((p), (o)))--#define token(p, ch) \- (((p)->flags & JV_PARSE_STREAMING) ? stream_token((p), (ch)) : parse_token((p), (ch)))--#define check_truncation(p) \- (((p)->flags & JV_PARSE_STREAMING) ? stream_check_truncation((p)) : parse_check_truncation((p)))--#define is_top_num(p) \- (((p)->flags & JV_PARSE_STREAMING) ? stream_is_top_num((p)) : parse_is_top_num((p)))--static pfunc scan(struct jv_parser* p, char ch, jv* out) {- p->column++;- if (ch == '\n') {- p->line++;- p->column = 0;- }- if (ch == '\036' /* ASCII RS; see draft-ietf-json-sequence-07 */) {- if (check_truncation(p)) {- if (check_literal(p) == 0 && is_top_num(p))- return "Potentially truncated top-level numeric value";- return "Truncated value";- }- TRY(check_literal(p));- if (p->st == JV_PARSER_NORMAL && check_done(p, out))- return OK;- // shouldn't happen?- assert(!jv_is_valid(*out));- parser_reset(p);- jv_free(*out);- *out = jv_invalid();- return OK;- }- presult answer = 0;- p->last_ch_was_ws = 0;- if (p->st == JV_PARSER_NORMAL) {- chclass cls = classify(ch);- if (cls == WHITESPACE)- p->last_ch_was_ws = 1;- if (cls != LITERAL) {- TRY(check_literal(p));- if (check_done(p, out)) answer = OK;- }- switch (cls) {- case LITERAL:- tokenadd(p, ch);- break;- case WHITESPACE:- break;- case QUOTE:- p->st = JV_PARSER_STRING;- break;- case STRUCTURE:- TRY(token(p, ch));- break;- case INVALID:- return "Invalid character";- }- if (check_done(p, out)) answer = OK;- } else {- if (ch == '"' && p->st == JV_PARSER_STRING) {- TRY(found_string(p));- p->st = JV_PARSER_NORMAL;- if (check_done(p, out)) answer = OK;- } else {- tokenadd(p, ch);- if (ch == '\\' && p->st == JV_PARSER_STRING) {- p->st = JV_PARSER_STRING_ESCAPE;- } else {- p->st = JV_PARSER_STRING;- }- }- }- return answer;-}--struct jv_parser* jv_parser_new(int flags) {- struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));- parser_init(p, flags);- p->flags = flags;- return p;-}--void jv_parser_free(struct jv_parser* p) {- parser_free(p);- jv_mem_free(p);-}--static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};--int jv_parser_remaining(struct jv_parser* p) {- if (p->curr_buf == 0)- return 0;- return (p->curr_buf_length - p->curr_buf_pos);-}--void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {- assert((p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length)- && "previous buffer not exhausted");- while (length > 0 && p->bom_strip_position < sizeof(UTF8_BOM)) {- if ((unsigned char)*buf == UTF8_BOM[p->bom_strip_position]) {- // matched a BOM character- buf++;- length--;- p->bom_strip_position++;- } else {- if (p->bom_strip_position == 0) {- // no BOM in this document- p->bom_strip_position = sizeof(UTF8_BOM);- } else {- // malformed BOM (prefix present, rest missing)- p->bom_strip_position = 0xff;- }- }- }- p->curr_buf = buf;- p->curr_buf_length = length;- p->curr_buf_pos = 0;- p->curr_buf_is_partial = is_partial;-}--static jv make_error(struct jv_parser*, const char *, ...) JV_PRINTF_LIKE(2, 3);--static jv make_error(struct jv_parser* p, const char *fmt, ...) {- va_list ap;- va_start(ap, fmt);- jv e = jv_string_vfmt(fmt, ap);- va_end(ap);- if ((p->flags & JV_PARSE_STREAM_ERRORS))- return JV_ARRAY(e, jv_copy(p->path));- return jv_invalid_with_msg(e);-}--jv jv_parser_next(struct jv_parser* p) {- if (p->eof)- return jv_invalid();- if (!p->curr_buf)- return jv_invalid(); // Need a buffer- if (p->bom_strip_position == 0xff) {- if (!(p->flags & JV_PARSE_SEQ))- return jv_invalid_with_msg(jv_string("Malformed BOM"));- p->st =JV_PARSER_WAITING_FOR_RS;- parser_reset(p);- }- jv value = jv_invalid();- if ((p->flags & JV_PARSE_STREAMING) && stream_check_done(p, &value))- return value;- char ch;- presult msg = 0;- while (!msg && p->curr_buf_pos < p->curr_buf_length) {- ch = p->curr_buf[p->curr_buf_pos++];- if (p->st == JV_PARSER_WAITING_FOR_RS) {- if (ch == '\n') {- p->line++;- p->column = 0;- } else {- p->column++;- }- if (ch == '\036')- p->st = JV_PARSER_NORMAL;- continue; // need to resync, wait for RS- }- msg = scan(p, ch, &value);- }- if (msg == OK) {- return value;- } else if (msg) {- jv_free(value);- if (ch != '\036' && (p->flags & JV_PARSE_SEQ)) {- // Skip to the next RS- p->st = JV_PARSER_WAITING_FOR_RS;- value = make_error(p, "%s at line %d, column %d (need RS to resync)", msg, p->line, p->column);- parser_reset(p);- return value;- }- value = make_error(p, "%s at line %d, column %d", msg, p->line, p->column);- parser_reset(p);- if (!(p->flags & JV_PARSE_SEQ)) {- // We're not parsing a JSON text sequence; throw this buffer away.- // XXX We should fail permanently here.- p->curr_buf = 0;- p->curr_buf_pos = 0;- } // Else ch must be RS; don't clear buf so we can start parsing again after this ch- return value;- } else if (p->curr_buf_is_partial) {- assert(p->curr_buf_pos == p->curr_buf_length);- // need another buffer- return jv_invalid();- } else {- // at EOF- p->eof = 1;- assert(p->curr_buf_pos == p->curr_buf_length);- jv_free(value);- if (p->st == JV_PARSER_WAITING_FOR_RS)- return make_error(p, "Unfinished abandoned text at EOF at line %d, column %d", p->line, p->column);- if (p->st != JV_PARSER_NORMAL) {- value = make_error(p, "Unfinished string at EOF at line %d, column %d", p->line, p->column);- parser_reset(p);- p->st = JV_PARSER_WAITING_FOR_RS;- return value;- }- if ((msg = check_literal(p))) {- value = make_error(p, "%s at EOF at line %d, column %d", msg, p->line, p->column);- parser_reset(p);- p->st = JV_PARSER_WAITING_FOR_RS;- return value;- }- if (((p->flags & JV_PARSE_STREAMING) && p->stacklen != 0) ||- (!(p->flags & JV_PARSE_STREAMING) && p->stackpos != 0)) {- value = make_error(p, "Unfinished JSON term at EOF at line %d, column %d", p->line, p->column);- parser_reset(p);- p->st = JV_PARSER_WAITING_FOR_RS;- return value;- }- // p->next is either invalid (nothing here, but no syntax error)- // or valid (this is the value). either way it's the thing to return- if ((p->flags & JV_PARSE_STREAMING) && jv_is_valid(p->next)) {- value = JV_ARRAY(jv_copy(p->path), p->next); // except in streaming mode we've got to make it [path,value]- } else {- value = p->next;- }- p->next = jv_invalid();- if ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && jv_get_kind(value) == JV_KIND_NUMBER) {- jv_free(value);- return make_error(p, "Potentially truncated top-level numeric value at EOF at line %d, column %d", p->line, p->column);- }- return value;- }-}--jv jv_parse_sized(const char* string, int length) {- struct jv_parser parser;- parser_init(&parser, 0);- 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_parse(const char* string) {- return jv_parse_sized(string, strlen(string));-}
jv_print.c
@@ -1,348 +0,0 @@-#include <assert.h>-#include <stdio.h>-#include <float.h>-#include <string.h>--#ifdef WIN32-#include <windows.h>-#include <io.h>-#include <fileapi.h>-#endif--#include "jv.h"-#include "jv_dtoa.h"-#include "jv_unicode.h"--#define ESC "\033"-#define COL(c) (ESC "[" c "m")-#define COLRESET (ESC "[0m")--// Color table. See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors-// for how to choose these.-static const jv_kind color_kinds[] =- {JV_KIND_NULL, JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER,- JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT};-static const char* const colors[] =- {COL("1;30"), COL("0;39"), COL("0;39"), COL("0;39"),- COL("0;32"), COL("1;39"), COL("1;39")};-#define FIELD_COLOR COL("34;1")--static void put_buf(const char *s, int len, FILE *fout, jv *strout, int is_tty) {- if (strout) {- *strout = jv_string_append_buf(*strout, s, len);- } else {-#ifdef WIN32- /* See util.h */- if (is_tty)- WriteFile((HANDLE)_get_osfhandle(fileno(fout)), s, len, NULL, NULL);- else- fwrite(s, 1, len, fout);-#else- fwrite(s, 1, len, fout);-#endif- }-}--static void put_char(char c, FILE* fout, jv* strout, int T) {- put_buf(&c, 1, fout, strout, T);-}--static void put_str(const char* s, FILE* fout, jv* strout, int T) {- put_buf(s, strlen(s), fout, strout, T);-}--static void put_indent(int n, int flags, FILE* fout, jv* strout, int T) {- if (flags & JV_PRINT_TAB) {- while (n--)- put_char('\t', fout, strout, T);- } else {- n *= ((flags & (JV_PRINT_SPACE0 | JV_PRINT_SPACE1 | JV_PRINT_SPACE2)) >> 8);- while (n--)- put_char(' ', fout, strout, T);- }-}--static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S, int T) {- assert(jv_get_kind(str) == JV_KIND_STRING);- const char* i = jv_string_value(str);- const char* end = i + jv_string_length_bytes(jv_copy(str));- const char* cstart;- int c = 0;- char buf[32];- put_char('"', F, S, T);- while ((i = jvp_utf8_next((cstart = i), end, &c))) {- assert(c != -1);- int unicode_escape = 0;- if (0x20 <= c && c <= 0x7E) {- // printable ASCII- if (c == '"' || c == '\\') {- put_char('\\', F, S, T);- }- put_char(c, F, S, T);- } else if (c < 0x20 || c == 0x7F) {- // ASCII control character- switch (c) {- case '\b':- put_char('\\', F, S, T);- put_char('b', F, S, T);- break;- case '\t':- put_char('\\', F, S, T);- put_char('t', F, S, T);- break;- case '\r':- put_char('\\', F, S, T);- put_char('r', F, S, T);- break;- case '\n':- put_char('\\', F, S, T);- put_char('n', F, S, T);- break;- case '\f':- put_char('\\', F, S, T);- put_char('f', F, S, T);- break;- default:- unicode_escape = 1;- break;- }- } else {- if (ascii_only) {- unicode_escape = 1;- } else {- put_buf(cstart, i - cstart, F, S, T);- }- }- if (unicode_escape) {- if (c <= 0xffff) {- snprintf(buf, sizeof(buf), "\\u%04x", c);- } else {- c -= 0x10000;- snprintf(buf, sizeof(buf), "\\u%04x\\u%04x",- 0xD800 | ((c & 0xffc00) >> 10),- 0xDC00 | (c & 0x003ff));- }- put_str(buf, F, S, T);- }- }- assert(c != -1);- put_char('"', F, S, T);-}--static void put_refcnt(struct dtoa_context* C, int refcnt, FILE *F, jv* S, int T){- char buf[JVP_DTOA_FMT_MAX_LEN];- put_char(' ', F, S, T);- put_char('(', F, S, T);- put_str(jvp_dtoa_fmt(C, buf, refcnt), F, S, T);- put_char(')', F, S, T);-}--static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {- char buf[JVP_DTOA_FMT_MAX_LEN];- const char* color = 0;- double refcnt = (flags & JV_PRINT_REFCOUNT) ? jv_get_refcnt(x) - 1 : -1;- if (flags & JV_PRINT_COLOR) {- for (unsigned i=0; i<sizeof(color_kinds)/sizeof(color_kinds[0]); i++) {- if (jv_get_kind(x) == color_kinds[i]) {- color = colors[i];- put_str(color, F, S, flags & JV_PRINT_ISATTY);- break;- }- }- }- switch (jv_get_kind(x)) {- default:- case JV_KIND_INVALID:- if (flags & JV_PRINT_INVALID) {- jv msg = jv_invalid_get_msg(jv_copy(x));- if (jv_get_kind(msg) == JV_KIND_STRING) {- put_str("<invalid:", F, S, flags & JV_PRINT_ISATTY);- jvp_dump_string(msg, flags | JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);- put_str(">", F, S, flags & JV_PRINT_ISATTY);- } else {- put_str("<invalid>", F, S, flags & JV_PRINT_ISATTY);- }- } else {- assert(0 && "Invalid value");- }- break;- case JV_KIND_NULL:- put_str("null", F, S, flags & JV_PRINT_ISATTY);- break;- case JV_KIND_FALSE:- put_str("false", F, S, flags & JV_PRINT_ISATTY);- break;- case JV_KIND_TRUE:- put_str("true", F, S, flags & JV_PRINT_ISATTY);- break;- case JV_KIND_NUMBER: {- double d = jv_number_value(x);- if (d != d) {- // JSON doesn't have NaN, so we'll render it as "null"- put_str("null", F, S, flags & JV_PRINT_ISATTY);- } else {- // Normalise infinities to something we can print in valid JSON- if (d > DBL_MAX) d = DBL_MAX;- if (d < -DBL_MAX) d = -DBL_MAX;- put_str(jvp_dtoa_fmt(C, buf, d), F, S, flags & JV_PRINT_ISATTY);- }- break;- }- case JV_KIND_STRING:- jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);- if (flags & JV_PRINT_REFCOUNT)- put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);- break;- case JV_KIND_ARRAY: {- if (jv_array_length(jv_copy(x)) == 0) {- put_str("[]", F, S, flags & JV_PRINT_ISATTY);- break;- }- put_str("[", F, S, flags & JV_PRINT_ISATTY);- if (flags & JV_PRINT_PRETTY) {- put_char('\n', F, S, flags & JV_PRINT_ISATTY);- put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);- }- jv_array_foreach(x, i, elem) {- if (i!=0) {- if (flags & JV_PRINT_PRETTY) {- put_str(",\n", F, S, flags & JV_PRINT_ISATTY);- put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);- } else {- put_str(",", F, S, flags & JV_PRINT_ISATTY);- }- }- jv_dump_term(C, elem, flags, indent + 1, F, S);- if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);- }- if (flags & JV_PRINT_PRETTY) {- put_char('\n', F, S, flags & JV_PRINT_ISATTY);- put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);- }- if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);- put_char(']', F, S, flags & JV_PRINT_ISATTY);- if (flags & JV_PRINT_REFCOUNT)- put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);- break;- }- case JV_KIND_OBJECT: {- if (jv_object_length(jv_copy(x)) == 0) {- put_str("{}", F, S, flags & JV_PRINT_ISATTY);- break;- }- put_char('{', F, S, flags & JV_PRINT_ISATTY);- if (flags & JV_PRINT_PRETTY) {- put_char('\n', F, S, flags & JV_PRINT_ISATTY);- put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);- }- int first = 1;- int i = 0;- jv keyset = jv_null();- while (1) {- jv key, value;- if (flags & JV_PRINT_SORTED) {- if (first) {- keyset = jv_keys(jv_copy(x));- i = 0;- } else {- i++;- }- if (i >= jv_array_length(jv_copy(keyset))) {- jv_free(keyset);- break;- }- key = jv_array_get(jv_copy(keyset), i);- value = jv_object_get(jv_copy(x), jv_copy(key));- } else {- if (first) {- i = jv_object_iter(x);- } else {- i = jv_object_iter_next(x, i);- }- if (!jv_object_iter_valid(x, i)) break;- key = jv_object_iter_key(x, i);- value = jv_object_iter_value(x, i);- }-- if (!first) {- if (flags & JV_PRINT_PRETTY){- put_str(",\n", F, S, flags & JV_PRINT_ISATTY);- put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);- } else {- put_str(",", F, S, flags & JV_PRINT_ISATTY);- }- }- if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);-- first = 0;- if (color) put_str(FIELD_COLOR, F, S, flags & JV_PRINT_ISATTY);- jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);- jv_free(key);- if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);-- if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);- put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S, flags & JV_PRINT_ISATTY);- if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);-- jv_dump_term(C, value, flags, indent + 1, F, S);- if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);- }- if (flags & JV_PRINT_PRETTY) {- put_char('\n', F, S, flags & JV_PRINT_ISATTY);- put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);- }- if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);- put_char('}', F, S, flags & JV_PRINT_ISATTY);- if (flags & JV_PRINT_REFCOUNT)- put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);- }- }- jv_free(x);- if (color) {- put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);- }-}--void jv_dumpf(jv x, FILE *f, int flags) {- struct dtoa_context C;- jvp_dtoa_context_init(&C);- jv_dump_term(&C, x, flags, 0, f, 0);- jvp_dtoa_context_free(&C);-}--void jv_dump(jv x, int flags) {- jv_dumpf(x, stdout, flags);-}--/* This one is nice for use in debuggers */-void jv_show(jv x, int flags) {- if (flags == -1)- flags = JV_PRINT_PRETTY | JV_PRINT_COLOR | JV_PRINT_INDENT_FLAGS(2);- jv_dumpf(jv_copy(x), stderr, flags | JV_PRINT_INVALID);- fflush(stderr);-}--jv jv_dump_string(jv x, int flags) {- struct dtoa_context C;- jvp_dtoa_context_init(&C);- jv s = jv_string("");- jv_dump_term(&C, x, flags, 0, 0, &s);- jvp_dtoa_context_free(&C);- return s;-}--char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) {- x = jv_dump_string(x,0);- const char* p = jv_string_value(x);- const size_t len = strlen(p);- strncpy(outbuf, p, bufsize);- outbuf[bufsize - 1] = 0;- if (len > bufsize - 1 && bufsize >= 4) {- // Indicate truncation with '...'- outbuf[bufsize - 2]='.';- outbuf[bufsize - 3]='.';- outbuf[bufsize - 4]='.';- }- jv_free(x);- return outbuf;-}
jv_unicode.c
@@ -1,97 +0,0 @@-#include <stdio.h>-#include <assert.h>-#include "jv_unicode.h"-#include "jv_utf8_tables.h"--const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {- assert(in <= end);- if (in == end) {- return 0;- }- int codepoint = -1;- unsigned char first = (unsigned char)in[0];- int length = utf8_coding_length[first];- if ((first & 0x80) == 0) {- /* Fast-path for ASCII */- codepoint = first;- length = 1;- } else if (length == 0 || length == UTF8_CONTINUATION_BYTE) {- /* Bad single byte - either an invalid byte or an out-of-place continuation byte */- length = 1;- } else if (in + length > end) {- /* String ends before UTF8 sequence ends */- length = end - in;- } else {- codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];- for (int i=1; i<length; i++) {- unsigned ch = (unsigned char)in[i];- if (utf8_coding_length[ch] != UTF8_CONTINUATION_BYTE){- /* Invalid UTF8 sequence - not followed by the right number of continuation bytes */- codepoint = -1;- length = i;- break;- }- codepoint = (codepoint << 6) | (ch & 0x3f);- }- if (codepoint < utf8_first_codepoint[length]) {- /* Overlong UTF8 sequence */- codepoint = -1;- }- if (0xD800 <= codepoint && codepoint <= 0xDFFF) {- /* Surrogate codepoints can't be encoded in UTF8 */- codepoint = -1;- }- if (codepoint > 0x10FFFF) {- /* Outside Unicode range */- codepoint = -1;- }- }- assert(length > 0);- *codepoint_ret = codepoint;- return in + length;-}--int jvp_utf8_is_valid(const char* in, const char* end) {- int codepoint;- while ((in = jvp_utf8_next(in, end, &codepoint))) {- if (codepoint == -1) return 0;- }- return 1;-}--/* Assumes startchar is the first byte of a valid character sequence */-int jvp_utf8_decode_length(char startchar) {- if ((startchar & 0x80) == 0) return 1; // 0___ ____- else if ((startchar & 0xE0) == 0xC0) return 2; // 110_ ____- else if ((startchar & 0xF0) == 0xE0) return 3; // 1110 ____- else return 4; // 1111 ____-}--int jvp_utf8_encode_length(int codepoint) {- if (codepoint <= 0x7F) return 1;- else if (codepoint <= 0x7FF) return 2;- else if (codepoint <= 0xFFFF) return 3;- else return 4;-}--int jvp_utf8_encode(int codepoint, char* out) {- assert(codepoint >= 0 && codepoint <= 0x10FFFF);- char* start = out;- if (codepoint <= 0x7F) {- *out++ = codepoint;- } else if (codepoint <= 0x7FF) {- *out++ = 0xC0 + ((codepoint & 0x7C0) >> 6);- *out++ = 0x80 + ((codepoint & 0x03F));- } else if(codepoint <= 0xFFFF) {- *out++ = 0xE0 + ((codepoint & 0xF000) >> 12);- *out++ = 0x80 + ((codepoint & 0x0FC0) >> 6);- *out++ = 0x80 + ((codepoint & 0x003F));- } else {- *out++ = 0xF0 + ((codepoint & 0x1C0000) >> 18);- *out++ = 0x80 + ((codepoint & 0x03F000) >> 12);- *out++ = 0x80 + ((codepoint & 0x000FC0) >> 6);- *out++ = 0x80 + ((codepoint & 0x00003F));- }- assert(out - start == jvp_utf8_encode_length(codepoint));- return out - start;-}
jv_unicode.h
@@ -1,11 +0,0 @@-#ifndef JV_UNICODE_H-#define JV_UNICODE_H--const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);-int jvp_utf8_is_valid(const char* in, const char* end);--int jvp_utf8_decode_length(char startchar);--int jvp_utf8_encode_length(int codepoint);-int jvp_utf8_encode(int codepoint, char* out);-#endif
jv_utf8_tables.h
@@ -1,37 +0,0 @@-#define UTF8_CONTINUATION_BYTE ((unsigned char)255)-static const unsigned char utf8_coding_length[] =- {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,- 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,- 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};-static const unsigned char utf8_coding_bits[] =- {0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,- 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,- 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,- 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,- 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,- 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};-static const int utf8_first_codepoint[] =- {0x00, 0x00, 0x80, 0x800, 0x10000};
lexer.c
@@ -1,2442 +0,0 @@-#line 2 "lexer.c"--#line 4 "lexer.c"--#define YY_INT_ALIGNED short int--/* A lexical scanner generated by flex */--#define FLEX_SCANNER-#define YY_FLEX_MAJOR_VERSION 2-#define YY_FLEX_MINOR_VERSION 5-#define YY_FLEX_SUBMINOR_VERSION 35-#if YY_FLEX_SUBMINOR_VERSION > 0-#define FLEX_BETA-#endif--/* First, we deal with platform-specific or compiler-specific issues. */--/* begin standard C headers. */-#include <stdio.h>-#include <string.h>-#include <errno.h>-#include <stdlib.h>--/* end standard C headers. */--/* flex integer type definitions */--#ifndef FLEXINT_H-#define FLEXINT_H--/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */--#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L--/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,- * if you want the limit (max/min) macros for int types. - */-#ifndef __STDC_LIMIT_MACROS-#define __STDC_LIMIT_MACROS 1-#endif--#include <inttypes.h>-typedef int8_t flex_int8_t;-typedef uint8_t flex_uint8_t;-typedef int16_t flex_int16_t;-typedef uint16_t flex_uint16_t;-typedef int32_t flex_int32_t;-typedef uint32_t flex_uint32_t;-#else-typedef signed char flex_int8_t;-typedef short int flex_int16_t;-typedef int flex_int32_t;-typedef unsigned char flex_uint8_t; -typedef unsigned short int flex_uint16_t;-typedef unsigned int flex_uint32_t;--/* Limits of integral types. */-#ifndef INT8_MIN-#define INT8_MIN (-128)-#endif-#ifndef INT16_MIN-#define INT16_MIN (-32767-1)-#endif-#ifndef INT32_MIN-#define INT32_MIN (-2147483647-1)-#endif-#ifndef INT8_MAX-#define INT8_MAX (127)-#endif-#ifndef INT16_MAX-#define INT16_MAX (32767)-#endif-#ifndef INT32_MAX-#define INT32_MAX (2147483647)-#endif-#ifndef UINT8_MAX-#define UINT8_MAX (255U)-#endif-#ifndef UINT16_MAX-#define UINT16_MAX (65535U)-#endif-#ifndef UINT32_MAX-#define UINT32_MAX (4294967295U)-#endif--#endif /* ! C99 */--#endif /* ! FLEXINT_H */--#ifdef __cplusplus--/* The "const" storage-class-modifier is valid. */-#define YY_USE_CONST--#else /* ! __cplusplus */--/* C99 requires __STDC__ to be defined as 1. */-#if defined (__STDC__)--#define YY_USE_CONST--#endif /* defined (__STDC__) */-#endif /* ! __cplusplus */--#ifdef YY_USE_CONST-#define yyconst const-#else-#define yyconst-#endif--/* Returned upon end-of-file. */-#define YY_NULL 0--/* Promotes a possibly negative, possibly signed char to an unsigned- * integer for use as an array index. If the signed char is negative,- * we want to instead treat it as an 8-bit unsigned char, hence the- * double cast.- */-#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)--/* An opaque pointer. */-#ifndef YY_TYPEDEF_YY_SCANNER_T-#define YY_TYPEDEF_YY_SCANNER_T-typedef void* yyscan_t;-#endif--/* For convenience, these vars (plus the bison vars far below)- are macros in the reentrant scanner. */-#define yyin yyg->yyin_r-#define yyout yyg->yyout_r-#define yyextra yyg->yyextra_r-#define yyleng yyg->yyleng_r-#define yytext yyg->yytext_r-#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)-#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)-#define yy_flex_debug yyg->yy_flex_debug_r--/* Enter a start condition. This macro really ought to take a parameter,- * but we do it the disgusting crufty way forced on us by the ()-less- * definition of BEGIN.- */-#define BEGIN yyg->yy_start = 1 + 2 *--/* Translate the current start state into a value that can be later handed- * to BEGIN to return to the state. The YYSTATE alias is for lex- * compatibility.- */-#define YY_START ((yyg->yy_start - 1) / 2)-#define YYSTATE YY_START--/* Action number for EOF rule of a given start state. */-#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)--/* Special action meaning "start processing a new file". */-#define YY_NEW_FILE jq_yyrestart(yyin ,yyscanner )--#define YY_END_OF_BUFFER_CHAR 0--/* Size of default input buffer. */-#ifndef YY_BUF_SIZE-#ifdef __ia64__-/* On IA-64, the buffer size is 16k, not 8k.- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.- * Ditto for the __ia64__ case accordingly.- */-#define YY_BUF_SIZE 32768-#else-#define YY_BUF_SIZE 16384-#endif /* __ia64__ */-#endif--/* The state buf must be large enough to hold one state per character in the main buffer.- */-#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))--#ifndef YY_TYPEDEF_YY_BUFFER_STATE-#define YY_TYPEDEF_YY_BUFFER_STATE-typedef struct yy_buffer_state *YY_BUFFER_STATE;-#endif--#define EOB_ACT_CONTINUE_SCAN 0-#define EOB_ACT_END_OF_FILE 1-#define EOB_ACT_LAST_MATCH 2-- #define YY_LESS_LINENO(n)- -/* Return all but the first "n" matched characters back to the input stream. */-#define yyless(n) \- do \- { \- /* Undo effects of setting up yytext. */ \- int yyless_macro_arg = (n); \- YY_LESS_LINENO(yyless_macro_arg);\- *yy_cp = yyg->yy_hold_char; \- YY_RESTORE_YY_MORE_OFFSET \- yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \- YY_DO_BEFORE_ACTION; /* set up yytext again */ \- } \- while ( 0 )--#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )--#ifndef YY_TYPEDEF_YY_SIZE_T-#define YY_TYPEDEF_YY_SIZE_T-typedef size_t yy_size_t;-#endif--#ifndef YY_STRUCT_YY_BUFFER_STATE-#define YY_STRUCT_YY_BUFFER_STATE-struct yy_buffer_state- {- FILE *yy_input_file;-- char *yy_ch_buf; /* input buffer */- char *yy_buf_pos; /* current position in input buffer */-- /* Size of input buffer in bytes, not including room for EOB- * characters.- */- yy_size_t yy_buf_size;-- /* Number of characters read into yy_ch_buf, not including EOB- * characters.- */- int yy_n_chars;-- /* Whether we "own" the buffer - i.e., we know we created it,- * and can realloc() it to grow it, and should free() it to- * delete it.- */- int yy_is_our_buffer;-- /* Whether this is an "interactive" input source; if so, and- * if we're using stdio for input, then we want to use getc()- * instead of fread(), to make sure we stop fetching input after- * each newline.- */- int yy_is_interactive;-- /* Whether we're considered to be at the beginning of a line.- * If so, '^' rules will be active on the next match, otherwise- * not.- */- int yy_at_bol;-- int yy_bs_lineno; /**< The line count. */- int yy_bs_column; /**< The column count. */- - /* Whether to try to fill the input buffer when we reach the- * end of it.- */- int yy_fill_buffer;-- int yy_buffer_status;--#define YY_BUFFER_NEW 0-#define YY_BUFFER_NORMAL 1- /* When an EOF's been seen but there's still some text to process- * then we mark the buffer as YY_EOF_PENDING, to indicate that we- * shouldn't try reading from the input source any more. We might- * still have a bunch of tokens to match, though, because of- * possible backing-up.- *- * When we actually see the EOF, we change the status to "new"- * (via jq_yyrestart()), so that the user can continue scanning by- * just pointing yyin at a new input file.- */-#define YY_BUFFER_EOF_PENDING 2-- };-#endif /* !YY_STRUCT_YY_BUFFER_STATE */--/* We provide macros for accessing buffer states in case in the- * future we want to put the buffer states in a more general- * "scanner state".- *- * Returns the top of the stack, or NULL.- */-#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \- ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \- : NULL)--/* Same as previous macro, but useful when we know that the buffer stack is not- * NULL or when we need an lvalue. For internal use only.- */-#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]--void jq_yyrestart (FILE *input_file ,yyscan_t yyscanner );-void jq_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );-void jq_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );-void jq_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );-void jq_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );-void jq_yypop_buffer_state (yyscan_t yyscanner );--static void jq_yyensure_buffer_stack (yyscan_t yyscanner );-static void jq_yy_load_buffer_state (yyscan_t yyscanner );-static void jq_yy_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );--#define YY_FLUSH_BUFFER jq_yy_flush_buffer(YY_CURRENT_BUFFER ,yyscanner)--YY_BUFFER_STATE jq_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );--void *jq_yyalloc (yy_size_t ,yyscan_t yyscanner );-void *jq_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );-void jq_yyfree (void * ,yyscan_t yyscanner );--#define yy_new_buffer jq_yy_create_buffer--#define yy_set_interactive(is_interactive) \- { \- if ( ! YY_CURRENT_BUFFER ){ \- jq_yyensure_buffer_stack (yyscanner); \- YY_CURRENT_BUFFER_LVALUE = \- jq_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \- } \- YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \- }--#define yy_set_bol(at_bol) \- { \- if ( ! YY_CURRENT_BUFFER ){\- jq_yyensure_buffer_stack (yyscanner); \- YY_CURRENT_BUFFER_LVALUE = \- jq_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \- } \- YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \- }--#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)--/* Begin user sect3 */--#define jq_yywrap(n) 1-#define YY_SKIP_YYWRAP--typedef unsigned char YY_CHAR;--typedef int yy_state_type;--#define yytext_ptr yytext_r--static yy_state_type yy_get_previous_state (yyscan_t yyscanner );-static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);-static int yy_get_next_buffer (yyscan_t yyscanner );-static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );--/* Done after the current pattern has been matched and before the- * corresponding action - sets up yytext.- */-#define YY_DO_BEFORE_ACTION \- yyg->yytext_ptr = yy_bp; \- yyleng = (size_t) (yy_cp - yy_bp); \- yyg->yy_hold_char = *yy_cp; \- *yy_cp = '\0'; \- yyg->yy_c_buf_p = yy_cp;--#define YY_NUM_RULES 49-#define YY_END_OF_BUFFER 50-/* This struct is not used in this scanner,- but its presence is necessary. */-struct yy_trans_info- {- flex_int32_t yy_verify;- flex_int32_t yy_nxt;- };-static yyconst flex_int16_t yy_accept[154] =- { 0,- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 0, 0, 50, 48, 47, 47, 48, 39, 1, 34,- 34, 35, 36, 34, 34, 34, 34, 34, 38, 34,- 34, 34, 48, 45, 45, 45, 45, 45, 45, 45,- 45, 45, 45, 45, 45, 45, 45, 34, 43, 43,- 41, 44, 47, 2, 1, 29, 27, 25, 26, 33,- 38, 46, 46, 18, 28, 0, 31, 3, 32, 37,- 45, 0, 45, 45, 4, 45, 45, 45, 45, 45,- 45, 9, 45, 45, 45, 45, 14, 45, 45, 45,- 24, 43, 42, 40, 42, 46, 0, 38, 30, 38,-- 0, 45, 13, 45, 45, 8, 45, 45, 15, 45,- 45, 45, 45, 45, 45, 45, 19, 0, 42, 45,- 45, 45, 45, 12, 11, 45, 45, 45, 45, 45,- 45, 10, 42, 45, 22, 20, 45, 45, 45, 21,- 45, 45, 42, 45, 45, 5, 45, 7, 16, 23,- 17, 6, 0- } ;--static yyconst flex_int32_t yy_ec[256] =- { 0,- 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,- 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 2, 5, 6, 7, 8, 9, 1, 1, 10,- 11, 12, 13, 14, 15, 16, 17, 18, 18, 18,- 18, 18, 18, 18, 18, 18, 18, 19, 20, 21,- 22, 23, 24, 25, 26, 26, 26, 26, 27, 26,- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,- 28, 29, 30, 1, 31, 1, 32, 33, 34, 35,-- 36, 37, 26, 38, 39, 26, 40, 41, 42, 43,- 44, 45, 26, 46, 47, 48, 49, 26, 26, 26,- 50, 26, 51, 52, 53, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,-- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1- } ;--static yyconst flex_int32_t yy_meta[54] =- { 0,- 1, 1, 2, 2, 1, 3, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 4, 1, 5, 6, 1,- 1, 1, 1, 1, 1, 7, 7, 1, 8, 1,- 9, 7, 7, 7, 7, 7, 7, 7, 7, 7,- 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,- 1, 1, 1- } ;--static yyconst flex_int16_t yy_base[167] =- { 0,- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 51, 52, 318, 319, 57, 59, 295, 319, 0, 319,- 294, 319, 319, 293, 292, 291, 47, 47, 50, 290,- 289, 288, 0, 290, 48, 51, 53, 52, 37, 59,- 57, 66, 56, 63, 68, 70, 72, 286, 0, 0,- 319, 80, 90, 319, 0, 319, 319, 319, 319, 95,- 99, 0, 106, 285, 319, 110, 319, 319, 319, 0,- 285, 281, 86, 77, 277, 97, 101, 111, 113, 115,- 117, 274, 119, 120, 118, 121, 270, 122, 123, 124,- 319, 0, 257, 319, 255, 0, 254, 249, 319, 245,-- 0, 125, 239, 126, 127, 237, 128, 134, 234, 136,- 143, 147, 148, 149, 152, 154, 232, 165, 212, 210,- 157, 159, 158, 209, 208, 160, 161, 162, 163, 164,- 166, 207, 196, 171, 205, 204, 174, 167, 175, 201,- 170, 176, 190, 190, 184, 199, 194, 198, 197, 85,- 78, 76, 319, 230, 239, 245, 250, 255, 264, 273,- 278, 283, 285, 290, 294, 298- } ;--static yyconst flex_int16_t yy_def[167] =- { 0,- 153, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 154, 154, 153, 153, 153, 153, 153, 153, 155, 153,- 153, 153, 153, 153, 153, 153, 156, 153, 153, 153,- 153, 153, 157, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 158, 158, 158, 158, 158, 153, 159, 159,- 153, 160, 153, 153, 155, 153, 153, 153, 153, 153,- 153, 161, 161, 153, 153, 153, 153, 153, 153, 157,- 158, 153, 158, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158,- 153, 159, 153, 153, 162, 161, 153, 161, 153, 153,-- 163, 158, 158, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 158, 158, 158, 158, 158, 160, 164, 158,- 158, 158, 158, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 165, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 166, 158, 158, 158, 158, 158, 158, 158,- 158, 158, 0, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153- } ;--static yyconst flex_int16_t yy_nxt[373] =- { 0,- 14, 15, 16, 14, 17, 18, 19, 20, 21, 22,- 23, 24, 25, 20, 26, 27, 28, 29, 20, 20,- 30, 31, 32, 20, 33, 34, 34, 22, 14, 23,- 35, 36, 37, 38, 39, 40, 41, 34, 42, 34,- 43, 44, 34, 45, 34, 46, 34, 47, 34, 34,- 22, 48, 23, 50, 50, 72, 51, 51, 53, 53,- 53, 53, 60, 64, 61, 61, 72, 61, 65, 72,- 72, 72, 78, 63, 72, 72, 66, 72, 73, 52,- 52, 72, 63, 77, 72, 66, 72, 85, 72, 94,- 72, 53, 53, 74, 72, 72, 72, 75, 76, 79,-- 81, 80, 82, 72, 72, 88, 86, 83, 84, 89,- 61, 103, 61, 87, 61, 72, 61, 90, 97, 72,- 97, 66, 97, 98, 97, 66, 102, 100, 95, 72,- 66, 72, 104, 72, 66, 72, 72, 72, 72, 72,- 72, 72, 72, 72, 72, 72, 72, 106, 105, 109,- 113, 107, 72, 112, 72, 114, 115, 122, 116, 108,- 123, 72, 110, 111, 124, 72, 72, 72, 121, 125,- 72, 126, 72, 117, 153, 72, 72, 72, 72, 72,- 72, 72, 72, 129, 72, 72, 127, 128, 72, 72,- 134, 137, 72, 72, 72, 136, 132, 130, 135, 142,-- 131, 144, 72, 140, 141, 148, 138, 145, 72, 147,- 139, 149, 72, 95, 146, 72, 72, 72, 118, 72,- 150, 151, 72, 72, 118, 72, 72, 72, 72, 152,- 49, 49, 49, 49, 49, 49, 49, 49, 49, 55,- 118, 55, 55, 55, 55, 55, 55, 55, 62, 62,- 72, 62, 72, 62, 70, 72, 70, 72, 70, 71,- 71, 71, 100, 71, 92, 92, 98, 92, 92, 92,- 92, 100, 92, 93, 93, 93, 93, 93, 93, 93,- 93, 93, 96, 118, 96, 118, 96, 119, 72, 119,- 119, 120, 72, 120, 133, 72, 133, 133, 143, 101,-- 143, 143, 93, 72, 93, 93, 99, 91, 72, 69,- 68, 67, 59, 58, 57, 56, 54, 153, 13, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153- } ;--static yyconst flex_int16_t yy_chk[373] =- { 0,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 11, 12, 39, 11, 12, 15, 15,- 16, 16, 27, 28, 27, 29, 35, 29, 28, 36,- 38, 37, 39, 27, 43, 41, 29, 40, 35, 11,- 12, 44, 27, 38, 42, 29, 45, 43, 46, 52,- 47, 53, 53, 36, 152, 74, 151, 36, 37, 40,-- 41, 40, 42, 150, 73, 46, 44, 42, 42, 47,- 60, 74, 60, 45, 61, 76, 61, 47, 63, 77,- 63, 60, 66, 63, 66, 61, 73, 66, 52, 78,- 60, 79, 76, 80, 61, 81, 85, 83, 84, 86,- 88, 89, 90, 102, 104, 105, 107, 78, 77, 80,- 85, 79, 108, 84, 110, 86, 88, 104, 89, 79,- 105, 111, 81, 83, 107, 112, 113, 114, 102, 108,- 115, 110, 116, 90, 118, 121, 123, 122, 126, 127,- 128, 129, 130, 113, 131, 138, 111, 112, 141, 134,- 121, 126, 137, 139, 142, 123, 116, 114, 122, 131,-- 115, 134, 145, 129, 130, 141, 127, 137, 144, 139,- 128, 142, 147, 118, 138, 149, 148, 146, 143, 140,- 144, 145, 136, 135, 133, 132, 125, 124, 120, 147,- 154, 154, 154, 154, 154, 154, 154, 154, 154, 155,- 119, 155, 155, 155, 155, 155, 155, 155, 156, 156,- 117, 156, 109, 156, 157, 106, 157, 103, 157, 158,- 158, 158, 100, 158, 159, 159, 98, 159, 159, 159,- 159, 97, 159, 160, 160, 160, 160, 160, 160, 160,- 160, 160, 161, 95, 161, 93, 161, 162, 87, 162,- 162, 163, 82, 163, 164, 75, 164, 164, 165, 72,-- 165, 165, 166, 71, 166, 166, 64, 48, 34, 32,- 31, 30, 26, 25, 24, 21, 17, 13, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153, 153, 153, 153, 153, 153, 153, 153, 153,- 153, 153- } ;--/* The intent behind this definition is that it'll catch- * any uses of REJECT which flex missed.- */-#define REJECT reject_used_but_not_detected-#define yymore() yymore_used_but_not_detected-#define YY_MORE_ADJ 0-#define YY_RESTORE_YY_MORE_OFFSET-#line 1 "lexer.l"-#line 2 "lexer.l"-#include <assert.h>-#include "jv_alloc.h"-#include "compile.h"--struct lexer_param;--#include "parser.h" /* Generated by bison. */--#define YY_USER_ACTION \- do { \- yylloc->start = jq_yyget_extra(yyscanner); \- yylloc->end = yylloc->start + yyleng; \- jq_yyset_extra(yylloc->end,yyscanner); \- } while (0);-------#line 25 "lexer.l"- static int enter(int opening, int state, yyscan_t yyscanner);- static int try_exit(int closing, int state, yyscan_t yyscanner);-#define YY_NO_INPUT 1-#line 601 "lexer.c"--#define INITIAL 0-#define IN_PAREN 1-#define IN_BRACKET 2-#define IN_BRACE 3-#define IN_QQINTERP 4-#define IN_QQSTRING 5--#ifndef YY_NO_UNISTD_H-/* Special case for "unistd.h", since it is non-ANSI. We include it way- * down here because we want the user's section 1 to have been scanned first.- * The user has a chance to override it with an option.- */-#include <unistd.h>-#endif--#define YY_EXTRA_TYPE int--/* Holds the entire state of the reentrant scanner. */-struct yyguts_t- {-- /* User-defined. Not touched by flex. */- YY_EXTRA_TYPE yyextra_r;-- /* The rest are the same as the globals declared in the non-reentrant scanner. */- FILE *yyin_r, *yyout_r;- size_t yy_buffer_stack_top; /**< index of top of stack. */- size_t yy_buffer_stack_max; /**< capacity of stack. */- YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */- char yy_hold_char;- int yy_n_chars;- int yyleng_r;- char *yy_c_buf_p;- int yy_init;- int yy_start;- int yy_did_buffer_switch_on_eof;- int yy_start_stack_ptr;- int yy_start_stack_depth;- int *yy_start_stack;- yy_state_type yy_last_accepting_state;- char* yy_last_accepting_cpos;-- int yylineno_r;- int yy_flex_debug_r;-- char *yytext_r;- int yy_more_flag;- int yy_more_len;-- YYSTYPE * yylval_r;-- YYLTYPE * yylloc_r;-- }; /* end struct yyguts_t */--static int yy_init_globals (yyscan_t yyscanner );-- /* This must go here because YYSTYPE and YYLTYPE are included- * from bison output in section 1.*/- # define yylval yyg->yylval_r- - # define yylloc yyg->yylloc_r- -int jq_yylex_init (yyscan_t* scanner);--int jq_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);--/* Accessor methods to globals.- These are made visible to non-reentrant scanners for convenience. */--int jq_yylex_destroy (yyscan_t yyscanner );--int jq_yyget_debug (yyscan_t yyscanner );--void jq_yyset_debug (int debug_flag ,yyscan_t yyscanner );--YY_EXTRA_TYPE jq_yyget_extra (yyscan_t yyscanner );--void jq_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );--FILE *jq_yyget_in (yyscan_t yyscanner );--void jq_yyset_in (FILE * in_str ,yyscan_t yyscanner );--FILE *jq_yyget_out (yyscan_t yyscanner );--void jq_yyset_out (FILE * out_str ,yyscan_t yyscanner );--int jq_yyget_leng (yyscan_t yyscanner );--char *jq_yyget_text (yyscan_t yyscanner );--int jq_yyget_lineno (yyscan_t yyscanner );--void jq_yyset_lineno (int line_number ,yyscan_t yyscanner );--YYSTYPE * jq_yyget_lval (yyscan_t yyscanner );--void jq_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );-- YYLTYPE *jq_yyget_lloc (yyscan_t yyscanner );- - void jq_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );- -/* Macros after this point can all be overridden by user definitions in- * section 1.- */--#ifndef YY_SKIP_YYWRAP-#ifdef __cplusplus-extern "C" int jq_yywrap (yyscan_t yyscanner );-#else-extern int jq_yywrap (yyscan_t yyscanner );-#endif-#endif--#ifndef yytext_ptr-static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);-#endif--#ifdef YY_NEED_STRLEN-static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);-#endif--#ifndef YY_NO_INPUT--#ifdef __cplusplus-static int yyinput (yyscan_t yyscanner );-#else-static int input (yyscan_t yyscanner );-#endif--#endif-- static void yy_push_state (int new_state ,yyscan_t yyscanner);- - static void yy_pop_state (yyscan_t yyscanner );- - static int yy_top_state (yyscan_t yyscanner );- -/* Amount of stuff to slurp up with each read. */-#ifndef YY_READ_BUF_SIZE-#ifdef __ia64__-/* On IA-64, the buffer size is 16k, not 8k */-#define YY_READ_BUF_SIZE 16384-#else-#define YY_READ_BUF_SIZE 8192-#endif /* __ia64__ */-#endif--/* Copy whatever the last rule matched to the standard output. */-#ifndef ECHO-/* This used to be an fputs(), but since the string might contain NUL's,- * we now use fwrite().- */-#define ECHO do { if (fwrite( yytext, yyleng, 1, yyout )) {} } while (0)-#endif--/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,- * is returned in "result".- */-#ifndef YY_INPUT-#define YY_INPUT(buf,result,max_size) \- if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \- { \- int c = '*'; \- size_t n; \- for ( n = 0; n < max_size && \- (c = getc( yyin )) != EOF && c != '\n'; ++n ) \- buf[n] = (char) c; \- if ( c == '\n' ) \- buf[n++] = (char) c; \- if ( c == EOF && ferror( yyin ) ) \- YY_FATAL_ERROR( "input in flex scanner failed" ); \- result = n; \- } \- else \- { \- errno=0; \- while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \- { \- if( errno != EINTR) \- { \- YY_FATAL_ERROR( "input in flex scanner failed" ); \- break; \- } \- errno=0; \- clearerr(yyin); \- } \- }\-\--#endif--/* No semi-colon after return; correct usage is to write "yyterminate();" -- * we don't want an extra ';' after the "return" because that will cause- * some compilers to complain about unreachable statements.- */-#ifndef yyterminate-#define yyterminate() return YY_NULL-#endif--/* Number of entries by which start-condition stack grows. */-#ifndef YY_START_STACK_INCR-#define YY_START_STACK_INCR 25-#endif--/* Report a fatal error. */-#ifndef YY_FATAL_ERROR-#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)-#endif--/* end tables serialization structures and prototypes */--/* Default declaration of generated scanner - a define so the user can- * easily add parameters.- */-#ifndef YY_DECL-#define YY_DECL_IS_OURS 1--extern int jq_yylex \- (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);--#define YY_DECL int jq_yylex \- (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)-#endif /* !YY_DECL */--/* Code executed at the beginning of each rule, after yytext and yyleng- * have been set up.- */-#ifndef YY_USER_ACTION-#define YY_USER_ACTION-#endif--/* Code executed at the end of each rule. */-#ifndef YY_BREAK-#define YY_BREAK break;-#endif--#define YY_RULE_SETUP \- YY_USER_ACTION--/** The main scanner function which does all the work.- */-YY_DECL-{- register yy_state_type yy_current_state;- register char *yy_cp, *yy_bp;- register int yy_act;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;--#line 38 "lexer.l"---#line 857 "lexer.c"-- yylval = yylval_param;-- yylloc = yylloc_param;-- if ( !yyg->yy_init )- {- yyg->yy_init = 1;--#ifdef YY_USER_INIT- YY_USER_INIT;-#endif-- if ( ! yyg->yy_start )- yyg->yy_start = 1; /* first start state */-- if ( ! yyin )- yyin = stdin;-- if ( ! yyout )- yyout = stdout;-- if ( ! YY_CURRENT_BUFFER ) {- jq_yyensure_buffer_stack (yyscanner);- YY_CURRENT_BUFFER_LVALUE =- jq_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);- }-- jq_yy_load_buffer_state(yyscanner );- }-- while ( 1 ) /* loops until end-of-file is reached */- {- yy_cp = yyg->yy_c_buf_p;-- /* Support of yytext. */- *yy_cp = yyg->yy_hold_char;-- /* yy_bp points to the position in yy_ch_buf of the start of- * the current run.- */- yy_bp = yy_cp;-- yy_current_state = yyg->yy_start;-yy_match:- do- {- register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];- if ( yy_accept[yy_current_state] )- {- yyg->yy_last_accepting_state = yy_current_state;- yyg->yy_last_accepting_cpos = yy_cp;- }- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )- {- yy_current_state = (int) yy_def[yy_current_state];- if ( yy_current_state >= 154 )- yy_c = yy_meta[(unsigned int) yy_c];- }- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];- ++yy_cp;- }- while ( yy_base[yy_current_state] != 319 );--yy_find_action:- yy_act = yy_accept[yy_current_state];- if ( yy_act == 0 )- { /* have to back up */- yy_cp = yyg->yy_last_accepting_cpos;- yy_current_state = yyg->yy_last_accepting_state;- yy_act = yy_accept[yy_current_state];- }-- YY_DO_BEFORE_ACTION;--do_action: /* This label is used only to access EOF actions. */-- switch ( yy_act )- { /* beginning of action switch */- case 0: /* must back up */- /* undo the effects of YY_DO_BEFORE_ACTION */- *yy_cp = yyg->yy_hold_char;- yy_cp = yyg->yy_last_accepting_cpos;- yy_current_state = yyg->yy_last_accepting_state;- goto yy_find_action;--case 1:-YY_RULE_SETUP-#line 40 "lexer.l"-{ /* comments */ }- YY_BREAK-case 2:-YY_RULE_SETUP-#line 42 "lexer.l"-{ return NEQ; }- YY_BREAK-case 3:-YY_RULE_SETUP-#line 43 "lexer.l"-{ return EQ; }- YY_BREAK-case 4:-YY_RULE_SETUP-#line 44 "lexer.l"-{ return AS; }- YY_BREAK-case 5:-YY_RULE_SETUP-#line 45 "lexer.l"-{ return IMPORT; }- YY_BREAK-case 6:-YY_RULE_SETUP-#line 46 "lexer.l"-{ return INCLUDE; }- YY_BREAK-case 7:-YY_RULE_SETUP-#line 47 "lexer.l"-{ return MODULE; }- YY_BREAK-case 8:-YY_RULE_SETUP-#line 48 "lexer.l"-{ return DEF; }- YY_BREAK-case 9:-YY_RULE_SETUP-#line 49 "lexer.l"-{ return IF; }- YY_BREAK-case 10:-YY_RULE_SETUP-#line 50 "lexer.l"-{ return THEN; }- YY_BREAK-case 11:-YY_RULE_SETUP-#line 51 "lexer.l"-{ return ELSE; }- YY_BREAK-case 12:-YY_RULE_SETUP-#line 52 "lexer.l"-{ return ELSE_IF; }- YY_BREAK-case 13:-YY_RULE_SETUP-#line 53 "lexer.l"-{ return AND; }- YY_BREAK-case 14:-YY_RULE_SETUP-#line 54 "lexer.l"-{ return OR; }- YY_BREAK-case 15:-YY_RULE_SETUP-#line 55 "lexer.l"-{ return END; }- YY_BREAK-case 16:-YY_RULE_SETUP-#line 56 "lexer.l"-{ return REDUCE; }- YY_BREAK-case 17:-YY_RULE_SETUP-#line 57 "lexer.l"-{ return FOREACH; }- YY_BREAK-case 18:-YY_RULE_SETUP-#line 58 "lexer.l"-{ return DEFINEDOR; }- YY_BREAK-case 19:-YY_RULE_SETUP-#line 59 "lexer.l"-{ return TRY; }- YY_BREAK-case 20:-YY_RULE_SETUP-#line 60 "lexer.l"-{ return CATCH; }- YY_BREAK-case 21:-YY_RULE_SETUP-#line 61 "lexer.l"-{ return LABEL; }- YY_BREAK-case 22:-YY_RULE_SETUP-#line 62 "lexer.l"-{ return BREAK; }- YY_BREAK-case 23:-YY_RULE_SETUP-#line 63 "lexer.l"-{ return LOC; }- YY_BREAK-case 24:-YY_RULE_SETUP-#line 64 "lexer.l"-{ return SETPIPE; }- YY_BREAK-case 25:-YY_RULE_SETUP-#line 65 "lexer.l"-{ return SETPLUS; }- YY_BREAK-case 26:-YY_RULE_SETUP-#line 66 "lexer.l"-{ return SETMINUS; }- YY_BREAK-case 27:-YY_RULE_SETUP-#line 67 "lexer.l"-{ return SETMULT; }- YY_BREAK-case 28:-YY_RULE_SETUP-#line 68 "lexer.l"-{ return SETDIV; }- YY_BREAK-case 29:-YY_RULE_SETUP-#line 69 "lexer.l"-{ return SETMOD; }- YY_BREAK-case 30:-YY_RULE_SETUP-#line 70 "lexer.l"-{ return SETDEFINEDOR; }- YY_BREAK-case 31:-YY_RULE_SETUP-#line 71 "lexer.l"-{ return LESSEQ; }- YY_BREAK-case 32:-YY_RULE_SETUP-#line 72 "lexer.l"-{ return GREATEREQ; }- YY_BREAK-case 33:-YY_RULE_SETUP-#line 73 "lexer.l"-{ return REC; }- YY_BREAK-case 34:-YY_RULE_SETUP-#line 74 "lexer.l"-{ return yytext[0];}- YY_BREAK-case 35:-YY_RULE_SETUP-#line 76 "lexer.l"-{- return enter(yytext[0], YY_START, yyscanner);-}- YY_BREAK-case 36:-YY_RULE_SETUP-#line 80 "lexer.l"-{- return try_exit(yytext[0], YY_START, yyscanner);-}- YY_BREAK-case 37:-YY_RULE_SETUP-#line 84 "lexer.l"-{- yylval->literal = jv_string_sized(yytext + 1, yyleng - 1); return FORMAT;-}- YY_BREAK-case 38:-YY_RULE_SETUP-#line 88 "lexer.l"-{- yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL;-}- YY_BREAK-case 39:-YY_RULE_SETUP-#line 92 "lexer.l"-{- yy_push_state(IN_QQSTRING, yyscanner);- return QQSTRING_START;-}- YY_BREAK--case 40:-YY_RULE_SETUP-#line 98 "lexer.l"-{- return enter(QQSTRING_INTERP_START, YY_START, yyscanner);- }- YY_BREAK-case 41:-YY_RULE_SETUP-#line 101 "lexer.l"-{- yy_pop_state(yyscanner);- return QQSTRING_END;- }- YY_BREAK-case 42:-/* rule 42 can match eol */-YY_RULE_SETUP-#line 105 "lexer.l"-{- /* pass escapes to the json parser */- jv escapes = jv_string_fmt("\"%.*s\"", (int)yyleng, yytext);- yylval->literal = jv_parse_sized(jv_string_value(escapes), jv_string_length_bytes(jv_copy(escapes)));- jv_free(escapes);- return QQSTRING_TEXT;- }- YY_BREAK-case 43:-/* rule 43 can match eol */-YY_RULE_SETUP-#line 112 "lexer.l"-{- yylval->literal = jv_string_sized(yytext, yyleng);- return QQSTRING_TEXT;- }- YY_BREAK-case 44:-YY_RULE_SETUP-#line 116 "lexer.l"-{- return INVALID_CHARACTER;- }- YY_BREAK--case 45:-YY_RULE_SETUP-#line 122 "lexer.l"-{ yylval->literal = jv_string(yytext); return IDENT;}- YY_BREAK-case 46:-YY_RULE_SETUP-#line 123 "lexer.l"-{ yylval->literal = jv_string(yytext+1); return FIELD;}- YY_BREAK-case 47:-/* rule 47 can match eol */-YY_RULE_SETUP-#line 125 "lexer.l"-{}- YY_BREAK-case 48:-YY_RULE_SETUP-#line 127 "lexer.l"-{ return INVALID_CHARACTER; }- YY_BREAK-case 49:-YY_RULE_SETUP-#line 129 "lexer.l"-YY_FATAL_ERROR( "flex scanner jammed" );- YY_BREAK-#line 1221 "lexer.c"-case YY_STATE_EOF(INITIAL):-case YY_STATE_EOF(IN_PAREN):-case YY_STATE_EOF(IN_BRACKET):-case YY_STATE_EOF(IN_BRACE):-case YY_STATE_EOF(IN_QQINTERP):-case YY_STATE_EOF(IN_QQSTRING):- yyterminate();-- case YY_END_OF_BUFFER:- {- /* Amount of text matched not including the EOB char. */- int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;-- /* Undo the effects of YY_DO_BEFORE_ACTION. */- *yy_cp = yyg->yy_hold_char;- YY_RESTORE_YY_MORE_OFFSET-- if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )- {- /* We're scanning a new file or input source. It's- * possible that this happened because the user- * just pointed yyin at a new source and called- * jq_yylex(). If so, then we have to assure- * consistency between YY_CURRENT_BUFFER and our- * globals. Here is the right place to do so, because- * this is the first action (other than possibly a- * back-up) that will match for the new input source.- */- yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;- YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;- YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;- }-- /* Note that here we test for yy_c_buf_p "<=" to the position- * of the first EOB in the buffer, since yy_c_buf_p will- * already have been incremented past the NUL character- * (since all states make transitions on EOB to the- * end-of-buffer state). Contrast this with the test- * in input().- */- if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )- { /* This was really a NUL. */- yy_state_type yy_next_state;-- yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;-- yy_current_state = yy_get_previous_state( yyscanner );-- /* Okay, we're now positioned to make the NUL- * transition. We couldn't have- * yy_get_previous_state() go ahead and do it- * for us because it doesn't know how to deal- * with the possibility of jamming (and we don't- * want to build jamming into it because then it- * will run more slowly).- */-- yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);-- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;-- if ( yy_next_state )- {- /* Consume the NUL. */- yy_cp = ++yyg->yy_c_buf_p;- yy_current_state = yy_next_state;- goto yy_match;- }-- else- {- yy_cp = yyg->yy_c_buf_p;- goto yy_find_action;- }- }-- else switch ( yy_get_next_buffer( yyscanner ) )- {- case EOB_ACT_END_OF_FILE:- {- yyg->yy_did_buffer_switch_on_eof = 0;-- if ( jq_yywrap(yyscanner ) )- {- /* Note: because we've taken care in- * yy_get_next_buffer() to have set up- * yytext, we can now set up- * yy_c_buf_p so that if some total- * hoser (like flex itself) wants to- * call the scanner after we return the- * YY_NULL, it'll still work - another- * YY_NULL will get returned.- */- yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;-- yy_act = YY_STATE_EOF(YY_START);- goto do_action;- }-- else- {- if ( ! yyg->yy_did_buffer_switch_on_eof )- YY_NEW_FILE;- }- break;- }-- case EOB_ACT_CONTINUE_SCAN:- yyg->yy_c_buf_p =- yyg->yytext_ptr + yy_amount_of_matched_text;-- yy_current_state = yy_get_previous_state( yyscanner );-- yy_cp = yyg->yy_c_buf_p;- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;- goto yy_match;-- case EOB_ACT_LAST_MATCH:- yyg->yy_c_buf_p =- &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];-- yy_current_state = yy_get_previous_state( yyscanner );-- yy_cp = yyg->yy_c_buf_p;- yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;- goto yy_find_action;- }- break;- }-- default:- YY_FATAL_ERROR(- "fatal flex scanner internal error--no action found" );- } /* end of action switch */- } /* end of scanning one token */-} /* end of jq_yylex */--/* yy_get_next_buffer - try to read in a new buffer- *- * Returns a code representing an action:- * EOB_ACT_LAST_MATCH -- * EOB_ACT_CONTINUE_SCAN - continue scanning from current position- * EOB_ACT_END_OF_FILE - end of file- */-static int yy_get_next_buffer (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;- register char *source = yyg->yytext_ptr;- register int number_to_move, i;- int ret_val;-- if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )- YY_FATAL_ERROR(- "fatal flex scanner internal error--end of buffer missed" );-- if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )- { /* Don't try to fill the buffer, so this is an EOF. */- if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )- {- /* We matched a single character, the EOB, so- * treat this as a final EOF.- */- return EOB_ACT_END_OF_FILE;- }-- else- {- /* We matched some text prior to the EOB, first- * process it.- */- return EOB_ACT_LAST_MATCH;- }- }-- /* Try to read more data. */-- /* First move last chars to start of buffer. */- number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;-- for ( i = 0; i < number_to_move; ++i )- *(dest++) = *(source++);-- if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )- /* don't do the read, it's not guaranteed to return an EOF,- * just force an EOF- */- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;-- else- {- int num_to_read =- YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;-- while ( num_to_read <= 0 )- { /* Not enough room in the buffer - grow it. */-- /* just a shorter name for the current buffer */- YY_BUFFER_STATE b = YY_CURRENT_BUFFER;-- int yy_c_buf_p_offset =- (int) (yyg->yy_c_buf_p - b->yy_ch_buf);-- if ( b->yy_is_our_buffer )- {- int new_size = b->yy_buf_size * 2;-- if ( new_size <= 0 )- b->yy_buf_size += b->yy_buf_size / 8;- else- b->yy_buf_size *= 2;-- b->yy_ch_buf = (char *)- /* Include room in for 2 EOB chars. */- jq_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );- }- else- /* Can't grow it, we don't own it. */- b->yy_ch_buf = 0;-- if ( ! b->yy_ch_buf )- YY_FATAL_ERROR(- "fatal error - scanner input buffer overflow" );-- yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];-- num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -- number_to_move - 1;-- }-- if ( num_to_read > YY_READ_BUF_SIZE )- num_to_read = YY_READ_BUF_SIZE;-- /* Read in more data. */- YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),- yyg->yy_n_chars, (size_t) num_to_read );-- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;- }-- if ( yyg->yy_n_chars == 0 )- {- if ( number_to_move == YY_MORE_ADJ )- {- ret_val = EOB_ACT_END_OF_FILE;- jq_yyrestart(yyin ,yyscanner);- }-- else- {- ret_val = EOB_ACT_LAST_MATCH;- YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =- YY_BUFFER_EOF_PENDING;- }- }-- else- ret_val = EOB_ACT_CONTINUE_SCAN;-- if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {- /* Extend the array by 50%, plus the number we really need. */- yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) jq_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );- if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )- YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );- }-- yyg->yy_n_chars += number_to_move;- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;- YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;-- yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];-- return ret_val;-}--/* yy_get_previous_state - get the state just before the EOB char was reached */-- static yy_state_type yy_get_previous_state (yyscan_t yyscanner)-{- register yy_state_type yy_current_state;- register char *yy_cp;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- yy_current_state = yyg->yy_start;-- for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )- {- register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);- if ( yy_accept[yy_current_state] )- {- yyg->yy_last_accepting_state = yy_current_state;- yyg->yy_last_accepting_cpos = yy_cp;- }- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )- {- yy_current_state = (int) yy_def[yy_current_state];- if ( yy_current_state >= 154 )- yy_c = yy_meta[(unsigned int) yy_c];- }- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];- }-- return yy_current_state;-}--/* yy_try_NUL_trans - try to make a transition on the NUL character- *- * synopsis- * next_state = yy_try_NUL_trans( current_state );- */- static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)-{- register int yy_is_jam;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */- register char *yy_cp = yyg->yy_c_buf_p;-- register YY_CHAR yy_c = 1;- if ( yy_accept[yy_current_state] )- {- yyg->yy_last_accepting_state = yy_current_state;- yyg->yy_last_accepting_cpos = yy_cp;- }- while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )- {- yy_current_state = (int) yy_def[yy_current_state];- if ( yy_current_state >= 154 )- yy_c = yy_meta[(unsigned int) yy_c];- }- yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];- yy_is_jam = (yy_current_state == 153);-- return yy_is_jam ? 0 : yy_current_state;-}--#ifndef YY_NO_INPUT-#ifdef __cplusplus- static int yyinput (yyscan_t yyscanner)-#else- static int input (yyscan_t yyscanner)-#endif--{- int c;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- *yyg->yy_c_buf_p = yyg->yy_hold_char;-- if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )- {- /* yy_c_buf_p now points to the character we want to return.- * If this occurs *before* the EOB characters, then it's a- * valid NUL; if not, then we've hit the end of the buffer.- */- if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )- /* This was really a NUL. */- *yyg->yy_c_buf_p = '\0';-- else- { /* need more input */- int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;- ++yyg->yy_c_buf_p;-- switch ( yy_get_next_buffer( yyscanner ) )- {- case EOB_ACT_LAST_MATCH:- /* This happens because yy_g_n_b()- * sees that we've accumulated a- * token and flags that we need to- * try matching the token before- * proceeding. But for input(),- * there's no matching to consider.- * So convert the EOB_ACT_LAST_MATCH- * to EOB_ACT_END_OF_FILE.- */-- /* Reset buffer status. */- jq_yyrestart(yyin ,yyscanner);-- /*FALLTHROUGH*/-- case EOB_ACT_END_OF_FILE:- {- if ( jq_yywrap(yyscanner ) )- return EOF;-- if ( ! yyg->yy_did_buffer_switch_on_eof )- YY_NEW_FILE;-#ifdef __cplusplus- return yyinput(yyscanner);-#else- return input(yyscanner);-#endif- }-- case EOB_ACT_CONTINUE_SCAN:- yyg->yy_c_buf_p = yyg->yytext_ptr + offset;- break;- }- }- }-- c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */- *yyg->yy_c_buf_p = '\0'; /* preserve yytext */- yyg->yy_hold_char = *++yyg->yy_c_buf_p;-- return c;-}-#endif /* ifndef YY_NO_INPUT */--/** Immediately switch to a different input stream.- * @param input_file A readable stream.- * @param yyscanner The scanner object.- * @note This function does not reset the start condition to @c INITIAL .- */- void jq_yyrestart (FILE * input_file , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- if ( ! YY_CURRENT_BUFFER ){- jq_yyensure_buffer_stack (yyscanner);- YY_CURRENT_BUFFER_LVALUE =- jq_yy_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);- }-- jq_yy_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);- jq_yy_load_buffer_state(yyscanner );-}--/** Switch to a different input buffer.- * @param new_buffer The new input buffer.- * @param yyscanner The scanner object.- */- void jq_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- /* TODO. We should be able to replace this entire function body- * with- * jq_yypop_buffer_state();- * jq_yypush_buffer_state(new_buffer);- */- jq_yyensure_buffer_stack (yyscanner);- if ( YY_CURRENT_BUFFER == new_buffer )- return;-- if ( YY_CURRENT_BUFFER )- {- /* Flush out information for old buffer. */- *yyg->yy_c_buf_p = yyg->yy_hold_char;- YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;- }-- YY_CURRENT_BUFFER_LVALUE = new_buffer;- jq_yy_load_buffer_state(yyscanner );-- /* We don't actually know whether we did this switch during- * EOF (jq_yywrap()) processing, but the only time this flag- * is looked at is after jq_yywrap() is called, so it's safe- * to go ahead and always set it.- */- yyg->yy_did_buffer_switch_on_eof = 1;-}--static void jq_yy_load_buffer_state (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;- yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;- yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;- yyg->yy_hold_char = *yyg->yy_c_buf_p;-}--/** Allocate and initialize an input buffer state.- * @param file A readable stream.- * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.- * @param yyscanner The scanner object.- * @return the allocated buffer state.- */- YY_BUFFER_STATE jq_yy_create_buffer (FILE * file, int size , yyscan_t yyscanner)-{- YY_BUFFER_STATE b;- - b = (YY_BUFFER_STATE) jq_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );- if ( ! b )- YY_FATAL_ERROR( "out of dynamic memory in jq_yy_create_buffer()" );-- b->yy_buf_size = size;-- /* yy_ch_buf has to be 2 characters longer than the size given because- * we need to put in 2 end-of-buffer characters.- */- b->yy_ch_buf = (char *) jq_yyalloc(b->yy_buf_size + 2 ,yyscanner );- if ( ! b->yy_ch_buf )- YY_FATAL_ERROR( "out of dynamic memory in jq_yy_create_buffer()" );-- b->yy_is_our_buffer = 1;-- jq_yy_init_buffer(b,file ,yyscanner);-- return b;-}--/** Destroy the buffer.- * @param b a buffer created with jq_yy_create_buffer()- * @param yyscanner The scanner object.- */- void jq_yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- if ( ! b )- return;-- if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */- YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;-- if ( b->yy_is_our_buffer )- jq_yyfree((void *) b->yy_ch_buf ,yyscanner );-- jq_yyfree((void *) b ,yyscanner );-}--#ifndef __cplusplus-extern int isatty (int );-#endif /* __cplusplus */- -/* Initializes or reinitializes a buffer.- * This function is sometimes called more than once on the same buffer,- * such as during a jq_yyrestart() or at EOF.- */- static void jq_yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)--{- int oerrno = errno;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- jq_yy_flush_buffer(b ,yyscanner);-- b->yy_input_file = file;- b->yy_fill_buffer = 1;-- /* If b is the current buffer, then jq_yy_init_buffer was _probably_- * called from jq_yyrestart() or through yy_get_next_buffer.- * In that case, we don't want to reset the lineno or column.- */- if (b != YY_CURRENT_BUFFER){- b->yy_bs_lineno = 1;- b->yy_bs_column = 0;- }-- b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;- - errno = oerrno;-}--/** Discard all buffered characters. On the next scan, YY_INPUT will be called.- * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.- * @param yyscanner The scanner object.- */- void jq_yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- if ( ! b )- return;-- b->yy_n_chars = 0;-- /* We always need two end-of-buffer characters. The first causes- * a transition to the end-of-buffer state. The second causes- * a jam in that state.- */- b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;- b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;-- b->yy_buf_pos = &b->yy_ch_buf[0];-- b->yy_at_bol = 1;- b->yy_buffer_status = YY_BUFFER_NEW;-- if ( b == YY_CURRENT_BUFFER )- jq_yy_load_buffer_state(yyscanner );-}--/** Pushes the new state onto the stack. The new state becomes- * the current state. This function will allocate the stack- * if necessary.- * @param new_buffer The new state.- * @param yyscanner The scanner object.- */-void jq_yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- if (new_buffer == NULL)- return;-- jq_yyensure_buffer_stack(yyscanner);-- /* This block is copied from jq_yy_switch_to_buffer. */- if ( YY_CURRENT_BUFFER )- {- /* Flush out information for old buffer. */- *yyg->yy_c_buf_p = yyg->yy_hold_char;- YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;- YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;- }-- /* Only push if top exists. Otherwise, replace top. */- if (YY_CURRENT_BUFFER)- yyg->yy_buffer_stack_top++;- YY_CURRENT_BUFFER_LVALUE = new_buffer;-- /* copied from jq_yy_switch_to_buffer. */- jq_yy_load_buffer_state(yyscanner );- yyg->yy_did_buffer_switch_on_eof = 1;-}--/** Removes and deletes the top of the stack, if present.- * The next element becomes the new top.- * @param yyscanner The scanner object.- */-void jq_yypop_buffer_state (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- if (!YY_CURRENT_BUFFER)- return;-- jq_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);- YY_CURRENT_BUFFER_LVALUE = NULL;- if (yyg->yy_buffer_stack_top > 0)- --yyg->yy_buffer_stack_top;-- if (YY_CURRENT_BUFFER) {- jq_yy_load_buffer_state(yyscanner );- yyg->yy_did_buffer_switch_on_eof = 1;- }-}--/* Allocates the stack if it does not exist.- * Guarantees space for at least one push.- */-static void jq_yyensure_buffer_stack (yyscan_t yyscanner)-{- int num_to_alloc;- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- if (!yyg->yy_buffer_stack) {-- /* First allocation is just for 2 elements, since we don't know if this- * scanner will even need a stack. We use 2 instead of 1 to avoid an- * immediate realloc on the next call.- */- num_to_alloc = 1;- yyg->yy_buffer_stack = (struct yy_buffer_state**)jq_yyalloc- (num_to_alloc * sizeof(struct yy_buffer_state*)- , yyscanner);- if ( ! yyg->yy_buffer_stack )- YY_FATAL_ERROR( "out of dynamic memory in jq_yyensure_buffer_stack()" );- - memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));- - yyg->yy_buffer_stack_max = num_to_alloc;- yyg->yy_buffer_stack_top = 0;- return;- }-- if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){-- /* Increase the buffer to prepare for a possible push. */- int grow_size = 8 /* arbitrary grow size */;-- num_to_alloc = yyg->yy_buffer_stack_max + grow_size;- yyg->yy_buffer_stack = (struct yy_buffer_state**)jq_yyrealloc- (yyg->yy_buffer_stack,- num_to_alloc * sizeof(struct yy_buffer_state*)- , yyscanner);- if ( ! yyg->yy_buffer_stack )- YY_FATAL_ERROR( "out of dynamic memory in jq_yyensure_buffer_stack()" );-- /* zero only the new slots.*/- memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));- yyg->yy_buffer_stack_max = num_to_alloc;- }-}--/** Setup the input buffer state to scan directly from a user-specified character buffer.- * @param base the character buffer- * @param size the size in bytes of the character buffer- * @param yyscanner The scanner object.- * @return the newly allocated buffer state object. - */-YY_BUFFER_STATE jq_yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)-{- YY_BUFFER_STATE b;- - if ( size < 2 ||- base[size-2] != YY_END_OF_BUFFER_CHAR ||- base[size-1] != YY_END_OF_BUFFER_CHAR )- /* They forgot to leave room for the EOB's. */- return 0;-- b = (YY_BUFFER_STATE) jq_yyalloc(sizeof( struct yy_buffer_state ) ,yyscanner );- if ( ! b )- YY_FATAL_ERROR( "out of dynamic memory in jq_yy_scan_buffer()" );-- b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */- b->yy_buf_pos = b->yy_ch_buf = base;- b->yy_is_our_buffer = 0;- b->yy_input_file = 0;- b->yy_n_chars = b->yy_buf_size;- b->yy_is_interactive = 0;- b->yy_at_bol = 1;- b->yy_fill_buffer = 0;- b->yy_buffer_status = YY_BUFFER_NEW;-- jq_yy_switch_to_buffer(b ,yyscanner );-- return b;-}--/** Setup the input buffer state to scan a string. The next call to jq_yylex() will- * scan from a @e copy of @a str.- * @param yystr a NUL-terminated string to scan- * @param yyscanner The scanner object.- * @return the newly allocated buffer state object.- * @note If you want to scan bytes that may contain NUL values, then use- * jq_yy_scan_bytes() instead.- */-YY_BUFFER_STATE jq_yy_scan_string (yyconst char * yystr , yyscan_t yyscanner)-{- - return jq_yy_scan_bytes(yystr,strlen(yystr) ,yyscanner);-}--/** Setup the input buffer state to scan the given bytes. The next call to jq_yylex() will- * scan from a @e copy of @a bytes.- * @param yybytes the byte buffer to scan- * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.- * @param yyscanner The scanner object.- * @return the newly allocated buffer state object.- */-YY_BUFFER_STATE jq_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)-{- YY_BUFFER_STATE b;- char *buf;- yy_size_t n;- int i;- - /* Get memory for full buffer, including space for trailing EOB's. */- n = _yybytes_len + 2;- buf = (char *) jq_yyalloc(n ,yyscanner );- if ( ! buf )- YY_FATAL_ERROR( "out of dynamic memory in jq_yy_scan_bytes()" );-- for ( i = 0; i < _yybytes_len; ++i )- buf[i] = yybytes[i];-- buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;-- b = jq_yy_scan_buffer(buf,n ,yyscanner);- if ( ! b )- YY_FATAL_ERROR( "bad buffer in jq_yy_scan_bytes()" );-- /* It's okay to grow etc. this buffer, and we should throw it- * away when we're done.- */- b->yy_is_our_buffer = 1;-- return b;-}-- static void yy_push_state (int new_state , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth )- {- yy_size_t new_size;-- yyg->yy_start_stack_depth += YY_START_STACK_INCR;- new_size = yyg->yy_start_stack_depth * sizeof( int );-- if ( ! yyg->yy_start_stack )- yyg->yy_start_stack = (int *) jq_yyalloc(new_size ,yyscanner );-- else- yyg->yy_start_stack = (int *) jq_yyrealloc((void *) yyg->yy_start_stack,new_size ,yyscanner );-- if ( ! yyg->yy_start_stack )- YY_FATAL_ERROR( "out of memory expanding start-condition stack" );- }-- yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START;-- BEGIN(new_state);-}-- static void yy_pop_state (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- if ( --yyg->yy_start_stack_ptr < 0 )- YY_FATAL_ERROR( "start-condition stack underflow" );-- BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]);-}-- static int yy_top_state (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yyg->yy_start_stack[yyg->yy_start_stack_ptr - 1];-}--#ifndef YY_EXIT_FAILURE-#define YY_EXIT_FAILURE 2-#endif--static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)-{- (void) fprintf( stderr, "%s\n", msg );- exit( YY_EXIT_FAILURE );-}--/* Redefine yyless() so it works in section 3 code. */--#undef yyless-#define yyless(n) \- do \- { \- /* Undo effects of setting up yytext. */ \- int yyless_macro_arg = (n); \- YY_LESS_LINENO(yyless_macro_arg);\- yytext[yyleng] = yyg->yy_hold_char; \- yyg->yy_c_buf_p = yytext + yyless_macro_arg; \- yyg->yy_hold_char = *yyg->yy_c_buf_p; \- *yyg->yy_c_buf_p = '\0'; \- yyleng = yyless_macro_arg; \- } \- while ( 0 )--/* Accessor methods (get/set functions) to struct members. */--/** Get the user-defined data for this scanner.- * @param yyscanner The scanner object.- */-YY_EXTRA_TYPE jq_yyget_extra (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yyextra;-}--/** Get the current line number.- * @param yyscanner The scanner object.- */-int jq_yyget_lineno (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- - if (! YY_CURRENT_BUFFER)- return 0;- - return yylineno;-}--/** Get the current column number.- * @param yyscanner The scanner object.- */-int jq_yyget_column (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- - if (! YY_CURRENT_BUFFER)- return 0;- - return yycolumn;-}--/** Get the input stream.- * @param yyscanner The scanner object.- */-FILE *jq_yyget_in (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yyin;-}--/** Get the output stream.- * @param yyscanner The scanner object.- */-FILE *jq_yyget_out (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yyout;-}--/** Get the length of the current token.- * @param yyscanner The scanner object.- */-int jq_yyget_leng (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yyleng;-}--/** Get the current token.- * @param yyscanner The scanner object.- */--char *jq_yyget_text (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yytext;-}--/** Set the user-defined data. This data is never touched by the scanner.- * @param user_defined The data to be associated with this scanner.- * @param yyscanner The scanner object.- */-void jq_yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yyextra = user_defined ;-}--/** Set the current line number.- * @param line_number- * @param yyscanner The scanner object.- */-void jq_yyset_lineno (int line_number , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- /* lineno is only valid if an input buffer exists. */- if (! YY_CURRENT_BUFFER )- yy_fatal_error( "jq_yyset_lineno called with no buffer" , yyscanner); - - yylineno = line_number;-}--/** Set the current column.- * @param line_number- * @param yyscanner The scanner object.- */-void jq_yyset_column (int column_no , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- /* column is only valid if an input buffer exists. */- if (! YY_CURRENT_BUFFER )- yy_fatal_error( "jq_yyset_column called with no buffer" , yyscanner); - - yycolumn = column_no;-}--/** Set the input stream. This does not discard the current- * input buffer.- * @param in_str A readable stream.- * @param yyscanner The scanner object.- * @see jq_yy_switch_to_buffer- */-void jq_yyset_in (FILE * in_str , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yyin = in_str ;-}--void jq_yyset_out (FILE * out_str , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yyout = out_str ;-}--int jq_yyget_debug (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yy_flex_debug;-}--void jq_yyset_debug (int bdebug , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yy_flex_debug = bdebug ;-}--/* Accessor methods for yylval and yylloc */--YYSTYPE * jq_yyget_lval (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yylval;-}--void jq_yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yylval = yylval_param;-}--YYLTYPE *jq_yyget_lloc (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- return yylloc;-}- -void jq_yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- yylloc = yylloc_param;-}- -/* User-visible API */--/* jq_yylex_init is special because it creates the scanner itself, so it is- * the ONLY reentrant function that doesn't take the scanner as the last argument.- * That's why we explicitly handle the declaration, instead of using our macros.- */--int jq_yylex_init(yyscan_t* ptr_yy_globals)--{- if (ptr_yy_globals == NULL){- errno = EINVAL;- return 1;- }-- *ptr_yy_globals = (yyscan_t) jq_yyalloc ( sizeof( struct yyguts_t ), NULL );-- if (*ptr_yy_globals == NULL){- errno = ENOMEM;- return 1;- }-- /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */- memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));-- return yy_init_globals ( *ptr_yy_globals );-}--/* jq_yylex_init_extra has the same functionality as jq_yylex_init, but follows the- * convention of taking the scanner as the last argument. Note however, that- * this is a *pointer* to a scanner, as it will be allocated by this call (and- * is the reason, too, why this function also must handle its own declaration).- * The user defined value in the first argument will be available to jq_yyalloc in- * the yyextra field.- */--int jq_yylex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )--{- struct yyguts_t dummy_yyguts;-- jq_yyset_extra (yy_user_defined, &dummy_yyguts);-- if (ptr_yy_globals == NULL){- errno = EINVAL;- return 1;- }- - *ptr_yy_globals = (yyscan_t) jq_yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );- - if (*ptr_yy_globals == NULL){- errno = ENOMEM;- return 1;- }- - /* By setting to 0xAA, we expose bugs in- yy_init_globals. Leave at 0x00 for releases. */- memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));- - jq_yyset_extra (yy_user_defined, *ptr_yy_globals);- - return yy_init_globals ( *ptr_yy_globals );-}--static int yy_init_globals (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;- /* Initialization is the same as for the non-reentrant scanner.- * This function is called from jq_yylex_destroy(), so don't allocate here.- */-- yyg->yy_buffer_stack = 0;- yyg->yy_buffer_stack_top = 0;- yyg->yy_buffer_stack_max = 0;- yyg->yy_c_buf_p = (char *) 0;- yyg->yy_init = 0;- yyg->yy_start = 0;-- yyg->yy_start_stack_ptr = 0;- yyg->yy_start_stack_depth = 0;- yyg->yy_start_stack = NULL;--/* Defined in main.c */-#ifdef YY_STDINIT- yyin = stdin;- yyout = stdout;-#else- yyin = (FILE *) 0;- yyout = (FILE *) 0;-#endif-- /* For future reference: Set errno on error, since we are called by- * jq_yylex_init()- */- return 0;-}--/* jq_yylex_destroy is for both reentrant and non-reentrant scanners. */-int jq_yylex_destroy (yyscan_t yyscanner)-{- struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;-- /* Pop the buffer stack, destroying each element. */- while(YY_CURRENT_BUFFER){- jq_yy_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );- YY_CURRENT_BUFFER_LVALUE = NULL;- jq_yypop_buffer_state(yyscanner);- }-- /* Destroy the stack itself. */- jq_yyfree(yyg->yy_buffer_stack ,yyscanner);- yyg->yy_buffer_stack = NULL;-- /* Destroy the start condition stack. */- jq_yyfree(yyg->yy_start_stack ,yyscanner );- yyg->yy_start_stack = NULL;-- /* Reset the globals. This is important in a non-reentrant scanner so the next time- * jq_yylex() is called, initialization will occur. */- yy_init_globals( yyscanner);-- /* Destroy the main struct (reentrant only). */- jq_yyfree ( yyscanner , yyscanner );- yyscanner = NULL;- return 0;-}--/*- * Internal utility routines.- */--#ifndef yytext_ptr-static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)-{- register int i;- for ( i = 0; i < n; ++i )- s1[i] = s2[i];-}-#endif--#ifdef YY_NEED_STRLEN-static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)-{- register int n;- for ( n = 0; s[n]; ++n )- ;-- return n;-}-#endif--#define YYTABLES_NAME "yytables"--#line 129 "lexer.l"---/* perhaps these should be calls... */-/*-"true" { return TRUE; }-"false" { return FALSE; }-"null" { return NULL; }-*/-static int try_exit(int c, int state, yyscan_t yyscanner) {- char match = 0;- int ret;- switch (state) {- case IN_PAREN: match = ret = ')'; break;- case IN_BRACKET: match = ret = ']'; break;- case IN_BRACE: match = ret = '}'; break;-- case IN_QQINTERP:- match = ')';- ret = QQSTRING_INTERP_END;- break;-- default:- // may not be the best error to give- return INVALID_CHARACTER;- }- assert(match);- if (match == c) {- yy_pop_state(yyscanner);- return ret;- } else {- // FIXME: should we pop? Give a better error at least- return INVALID_CHARACTER;- }-}--static int enter(int c, int currstate, yyscan_t yyscanner) {- int state = 0;- switch (c) {- case '(': state = IN_PAREN; break;- case '[': state = IN_BRACKET; break;- case '{': state = IN_BRACE; break;- case QQSTRING_INTERP_START: state = IN_QQINTERP; break;- }- assert(state);- yy_push_state(state, yyscanner);- return c;-}--void* jq_yyalloc(size_t sz,void* extra) {- return jv_mem_alloc(sz);-}-void* jq_yyrealloc(void* p,size_t sz,void* extra) {- return jv_mem_realloc(p, sz);-}-void jq_yyfree(void* p,void* extra) {- jv_mem_free(p);-}-
lexer.h
@@ -1,362 +0,0 @@-#ifndef jq_yyHEADER_H-#define jq_yyHEADER_H 1-#define jq_yyIN_HEADER 1--#line 6 "lexer.h"--#line 8 "lexer.h"--#define YY_INT_ALIGNED short int--/* A lexical scanner generated by flex */--#define FLEX_SCANNER-#define YY_FLEX_MAJOR_VERSION 2-#define YY_FLEX_MINOR_VERSION 5-#define YY_FLEX_SUBMINOR_VERSION 35-#if YY_FLEX_SUBMINOR_VERSION > 0-#define FLEX_BETA-#endif--/* First, we deal with platform-specific or compiler-specific issues. */--/* begin standard C headers. */-#include <stdio.h>-#include <string.h>-#include <errno.h>-#include <stdlib.h>--/* end standard C headers. */--/* flex integer type definitions */--#ifndef FLEXINT_H-#define FLEXINT_H--/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */--#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L--/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,- * if you want the limit (max/min) macros for int types. - */-#ifndef __STDC_LIMIT_MACROS-#define __STDC_LIMIT_MACROS 1-#endif--#include <inttypes.h>-typedef int8_t flex_int8_t;-typedef uint8_t flex_uint8_t;-typedef int16_t flex_int16_t;-typedef uint16_t flex_uint16_t;-typedef int32_t flex_int32_t;-typedef uint32_t flex_uint32_t;-#else-typedef signed char flex_int8_t;-typedef short int flex_int16_t;-typedef int flex_int32_t;-typedef unsigned char flex_uint8_t; -typedef unsigned short int flex_uint16_t;-typedef unsigned int flex_uint32_t;--/* Limits of integral types. */-#ifndef INT8_MIN-#define INT8_MIN (-128)-#endif-#ifndef INT16_MIN-#define INT16_MIN (-32767-1)-#endif-#ifndef INT32_MIN-#define INT32_MIN (-2147483647-1)-#endif-#ifndef INT8_MAX-#define INT8_MAX (127)-#endif-#ifndef INT16_MAX-#define INT16_MAX (32767)-#endif-#ifndef INT32_MAX-#define INT32_MAX (2147483647)-#endif-#ifndef UINT8_MAX-#define UINT8_MAX (255U)-#endif-#ifndef UINT16_MAX-#define UINT16_MAX (65535U)-#endif-#ifndef UINT32_MAX-#define UINT32_MAX (4294967295U)-#endif--#endif /* ! C99 */--#endif /* ! FLEXINT_H */--#ifdef __cplusplus--/* The "const" storage-class-modifier is valid. */-#define YY_USE_CONST--#else /* ! __cplusplus */--/* C99 requires __STDC__ to be defined as 1. */-#if defined (__STDC__)--#define YY_USE_CONST--#endif /* defined (__STDC__) */-#endif /* ! __cplusplus */--#ifdef YY_USE_CONST-#define yyconst const-#else-#define yyconst-#endif--/* An opaque pointer. */-#ifndef YY_TYPEDEF_YY_SCANNER_T-#define YY_TYPEDEF_YY_SCANNER_T-typedef void* yyscan_t;-#endif--/* For convenience, these vars (plus the bison vars far below)- are macros in the reentrant scanner. */-#define yyin yyg->yyin_r-#define yyout yyg->yyout_r-#define yyextra yyg->yyextra_r-#define yyleng yyg->yyleng_r-#define yytext yyg->yytext_r-#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)-#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)-#define yy_flex_debug yyg->yy_flex_debug_r--/* Size of default input buffer. */-#ifndef YY_BUF_SIZE-#ifdef __ia64__-/* On IA-64, the buffer size is 16k, not 8k.- * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.- * Ditto for the __ia64__ case accordingly.- */-#define YY_BUF_SIZE 32768-#else-#define YY_BUF_SIZE 16384-#endif /* __ia64__ */-#endif--#ifndef YY_TYPEDEF_YY_BUFFER_STATE-#define YY_TYPEDEF_YY_BUFFER_STATE-typedef struct yy_buffer_state *YY_BUFFER_STATE;-#endif--#ifndef YY_TYPEDEF_YY_SIZE_T-#define YY_TYPEDEF_YY_SIZE_T-typedef size_t yy_size_t;-#endif--#ifndef YY_STRUCT_YY_BUFFER_STATE-#define YY_STRUCT_YY_BUFFER_STATE-struct yy_buffer_state- {- FILE *yy_input_file;-- char *yy_ch_buf; /* input buffer */- char *yy_buf_pos; /* current position in input buffer */-- /* Size of input buffer in bytes, not including room for EOB- * characters.- */- yy_size_t yy_buf_size;-- /* Number of characters read into yy_ch_buf, not including EOB- * characters.- */- int yy_n_chars;-- /* Whether we "own" the buffer - i.e., we know we created it,- * and can realloc() it to grow it, and should free() it to- * delete it.- */- int yy_is_our_buffer;-- /* Whether this is an "interactive" input source; if so, and- * if we're using stdio for input, then we want to use getc()- * instead of fread(), to make sure we stop fetching input after- * each newline.- */- int yy_is_interactive;-- /* Whether we're considered to be at the beginning of a line.- * If so, '^' rules will be active on the next match, otherwise- * not.- */- int yy_at_bol;-- int yy_bs_lineno; /**< The line count. */- int yy_bs_column; /**< The column count. */- - /* Whether to try to fill the input buffer when we reach the- * end of it.- */- int yy_fill_buffer;-- int yy_buffer_status;-- };-#endif /* !YY_STRUCT_YY_BUFFER_STATE */--void jq_yyrestart (FILE *input_file ,yyscan_t yyscanner );-void jq_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );-void jq_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );-void jq_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );-void jq_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );-void jq_yypop_buffer_state (yyscan_t yyscanner );--YY_BUFFER_STATE jq_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );-YY_BUFFER_STATE jq_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );--void *jq_yyalloc (yy_size_t ,yyscan_t yyscanner );-void *jq_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );-void jq_yyfree (void * ,yyscan_t yyscanner );--/* Begin user sect3 */--#define jq_yywrap(n) 1-#define YY_SKIP_YYWRAP--#define yytext_ptr yytext_r--#ifdef YY_HEADER_EXPORT_START_CONDITIONS-#define INITIAL 0-#define IN_PAREN 1-#define IN_BRACKET 2-#define IN_BRACE 3-#define IN_QQINTERP 4-#define IN_QQSTRING 5--#endif--#ifndef YY_NO_UNISTD_H-/* Special case for "unistd.h", since it is non-ANSI. We include it way- * down here because we want the user's section 1 to have been scanned first.- * The user has a chance to override it with an option.- */-#include <unistd.h>-#endif--#define YY_EXTRA_TYPE int--int jq_yylex_init (yyscan_t* scanner);--int jq_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);--/* Accessor methods to globals.- These are made visible to non-reentrant scanners for convenience. */--int jq_yylex_destroy (yyscan_t yyscanner );--int jq_yyget_debug (yyscan_t yyscanner );--void jq_yyset_debug (int debug_flag ,yyscan_t yyscanner );--YY_EXTRA_TYPE jq_yyget_extra (yyscan_t yyscanner );--void jq_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );--FILE *jq_yyget_in (yyscan_t yyscanner );--void jq_yyset_in (FILE * in_str ,yyscan_t yyscanner );--FILE *jq_yyget_out (yyscan_t yyscanner );--void jq_yyset_out (FILE * out_str ,yyscan_t yyscanner );--int jq_yyget_leng (yyscan_t yyscanner );--char *jq_yyget_text (yyscan_t yyscanner );--int jq_yyget_lineno (yyscan_t yyscanner );--void jq_yyset_lineno (int line_number ,yyscan_t yyscanner );--YYSTYPE * jq_yyget_lval (yyscan_t yyscanner );--void jq_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );-- YYLTYPE *jq_yyget_lloc (yyscan_t yyscanner );- - void jq_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );- -/* Macros after this point can all be overridden by user definitions in- * section 1.- */--#ifndef YY_SKIP_YYWRAP-#ifdef __cplusplus-extern "C" int jq_yywrap (yyscan_t yyscanner );-#else-extern int jq_yywrap (yyscan_t yyscanner );-#endif-#endif--#ifndef yytext_ptr-static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);-#endif--#ifdef YY_NEED_STRLEN-static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);-#endif--#ifndef YY_NO_INPUT--#endif--/* Amount of stuff to slurp up with each read. */-#ifndef YY_READ_BUF_SIZE-#ifdef __ia64__-/* On IA-64, the buffer size is 16k, not 8k */-#define YY_READ_BUF_SIZE 16384-#else-#define YY_READ_BUF_SIZE 8192-#endif /* __ia64__ */-#endif--/* Number of entries by which start-condition stack grows. */-#ifndef YY_START_STACK_INCR-#define YY_START_STACK_INCR 25-#endif--/* Default declaration of generated scanner - a define so the user can- * easily add parameters.- */-#ifndef YY_DECL-#define YY_DECL_IS_OURS 1--extern int jq_yylex \- (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);--#define YY_DECL int jq_yylex \- (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)-#endif /* !YY_DECL */--/* yy_get_previous_state - get the state just before the EOB char was reached */--#undef YY_NEW_FILE-#undef YY_FLUSH_BUFFER-#undef yy_set_bol-#undef yy_new_buffer-#undef yy_set_interactive-#undef YY_DO_BEFORE_ACTION--#ifdef YY_DECL_IS_OURS-#undef YY_DECL_IS_OURS-#undef YY_DECL-#endif--#line 129 "lexer.l"---#line 361 "lexer.h"-#undef jq_yyIN_HEADER-#endif /* jq_yyHEADER_H */
lexer.l
@@ -1,184 +0,0 @@-%{-#include <assert.h>-#include "jv_alloc.h"-#include "compile.h"--struct lexer_param;--#include "parser.h" /* Generated by bison. */--#define YY_USER_ACTION \- do { \- yylloc->start = yyget_extra(yyscanner); \- yylloc->end = yylloc->start + yyleng; \- yyset_extra(yylloc->end, yyscanner); \- } while (0);--%}--%s IN_PAREN-%s IN_BRACKET-%s IN_BRACE-%s IN_QQINTERP-%x IN_QQSTRING-%{- static int enter(int opening, int state, yyscan_t yyscanner);- static int try_exit(int closing, int state, yyscan_t yyscanner);-%}--%option noyywrap nounput noinput nodefault-%option noyyalloc noyyrealloc noyyfree-%option reentrant-%option extra-type="int"-%option bison-bridge bison-locations-%option prefix="jq_yy"-%option stack---%%--"#"[^\r\n]* { /* comments */ }--"!=" { return NEQ; }-"==" { return EQ; }-"as" { return AS; }-"import" { return IMPORT; }-"include" { return INCLUDE; }-"module" { return MODULE; }-"def" { return DEF; }-"if" { return IF; }-"then" { return THEN; }-"else" { return ELSE; }-"elif" { return ELSE_IF; }-"and" { return AND; }-"or" { return OR; }-"end" { return END; }-"reduce" { return REDUCE; }-"foreach" { return FOREACH; }-"//" { return DEFINEDOR; }-"try" { return TRY; }-"catch" { return CATCH; }-"label" { return LABEL; }-"break" { return BREAK; }-"__loc__" { return LOC; }-"|=" { return SETPIPE; }-"+=" { return SETPLUS; }-"-=" { return SETMINUS; }-"*=" { return SETMULT; }-"/=" { return SETDIV; }-"%=" { return SETMOD; }-"//=" { return SETDEFINEDOR; }-"<=" { return LESSEQ; }-">=" { return GREATEREQ; }-".." { return REC; }-"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}--"["|"{"|"(" {- return enter(yytext[0], YY_START, yyscanner);-}--"]"|"}"|")" {- return try_exit(yytext[0], YY_START, yyscanner);-}--"@"[a-zA-Z0-9_]+ {- yylval->literal = jv_string_sized(yytext + 1, yyleng - 1); return FORMAT;-}--[0-9.]+([eE][+-]?[0-9]+)? {- yylval->literal = jv_parse_sized(yytext, yyleng); return LITERAL;-}--"\"" {- yy_push_state(IN_QQSTRING, yyscanner);- return QQSTRING_START;-}--<IN_QQSTRING>{- "\\(" {- return enter(QQSTRING_INTERP_START, YY_START, yyscanner);- }- "\"" {- yy_pop_state(yyscanner);- return QQSTRING_END;- }- (\\[^u(]|\\u[a-zA-Z0-9]{0,4})+ {- /* pass escapes to the json parser */- jv escapes = jv_string_fmt("\"%.*s\"", (int)yyleng, yytext);- yylval->literal = jv_parse_sized(jv_string_value(escapes), jv_string_length_bytes(jv_copy(escapes)));- jv_free(escapes);- return QQSTRING_TEXT;- }- [^\\\"]+ {- yylval->literal = jv_string_sized(yytext, yyleng);- return QQSTRING_TEXT;- }- . {- return INVALID_CHARACTER;- }-}---([a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}-\.[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext+1); return FIELD;}--[ \n\t]+ {}--. { return INVALID_CHARACTER; }--%%-/* perhaps these should be calls... */-/*-"true" { return TRUE; }-"false" { return FALSE; }-"null" { return NULL; }-*/-static int try_exit(int c, int state, yyscan_t yyscanner) {- char match = 0;- int ret;- switch (state) {- case IN_PAREN: match = ret = ')'; break;- case IN_BRACKET: match = ret = ']'; break;- case IN_BRACE: match = ret = '}'; break;-- case IN_QQINTERP:- match = ')';- ret = QQSTRING_INTERP_END;- break;-- default:- // may not be the best error to give- return INVALID_CHARACTER;- }- assert(match);- if (match == c) {- yy_pop_state(yyscanner);- return ret;- } else {- // FIXME: should we pop? Give a better error at least- return INVALID_CHARACTER;- }-}--static int enter(int c, int currstate, yyscan_t yyscanner) {- int state = 0;- switch (c) {- case '(': state = IN_PAREN; break;- case '[': state = IN_BRACKET; break;- case '{': state = IN_BRACE; break;- case QQSTRING_INTERP_START: state = IN_QQINTERP; break;- }- assert(state);- yy_push_state(state, yyscanner);- return c;-}--void* yyalloc(size_t sz, void* extra) {- return jv_mem_alloc(sz);-}-void* yyrealloc(void* p, size_t sz, void* extra) {- return jv_mem_realloc(p, sz);-}-void yyfree(void* p, void* extra) {- jv_mem_free(p);-}
libm.h
@@ -1,151 +0,0 @@-#ifdef HAVE_ACOS-LIBM_DD(acos)-#else-LIBM_DD_NO(acos)-#endif-#ifdef HAVE_ACOSH-LIBM_DD(acosh)-#else-LIBM_DD_NO(acosh)-#endif-#ifdef HAVE_ASIN-LIBM_DD(asin)-#else-LIBM_DD_NO(asin)-#endif-#ifdef HAVE_ASINH-LIBM_DD(asinh)-#else-LIBM_DD_NO(asinh)-#endif-#ifdef HAVE_ATAN-LIBM_DD(atan)-#else-LIBM_DD_NO(atan)-#endif-#ifdef HAVE_ATAN2-LIBM_DDD(atan2)-#else-LIBM_DDD_NO(atan2)-#endif-#ifdef HAVE_ATANH-LIBM_DD(atanh)-#else-LIBM_DD_NO(atanh)-#endif-#ifdef HAVE_CBRT-LIBM_DD(cbrt)-#else-LIBM_DD_NO(cbrt)-#endif-#ifdef HAVE_COS-LIBM_DD(cos)-#else-LIBM_DD_NO(cos)-#endif-#ifdef HAVE_COSH-LIBM_DD(cosh)-#else-LIBM_DD_NO(cosh)-#endif-#ifdef HAVE_EXP-LIBM_DD(exp)-#else-LIBM_DD_NO(exp)-#endif-#ifdef HAVE_EXP2-LIBM_DD(exp2)-#else-LIBM_DD_NO(exp2)-#endif-#ifdef HAVE_FLOOR-LIBM_DD(floor)-#else-LIBM_DD_NO(floor)-#endif-#ifdef HAVE_HYPOT-LIBM_DDD(hypot)-#else-LIBM_DDD_NO(hypot)-#endif-#ifdef HAVE_J0-LIBM_DD(j0)-#else-LIBM_DD_NO(j0)-#endif-#ifdef HAVE_J1-LIBM_DD(j1)-#else-LIBM_DD_NO(j1)-#endif-#ifdef HAVE_LOG-LIBM_DD(log)-#else-LIBM_DD_NO(log)-#endif-#ifdef HAVE_LOG10-LIBM_DD(log10)-#else-LIBM_DD_NO(log10)-#endif-#ifdef HAVE_LOG2-LIBM_DD(log2)-#else-LIBM_DD_NO(log2)-#endif-#ifdef HAVE_POW-LIBM_DDD(pow)-#else-LIBM_DDD_NO(pow)-#endif-#ifdef HAVE_REMAINDER-LIBM_DDD(remainder)-#else-LIBM_DDD_NO(remainder)-#endif-#ifdef HAVE_SIN-LIBM_DD(sin)-#else-LIBM_DD_NO(sin)-#endif-#ifdef HAVE_SINH-LIBM_DD(sinh)-#else-LIBM_DD_NO(sinh)-#endif-#ifdef HAVE_SQRT-LIBM_DD(sqrt)-#else-LIBM_DD_NO(sqrt)-#endif-#ifdef HAVE_TAN-LIBM_DD(tan)-#else-LIBM_DD_NO(tan)-#endif-#ifdef HAVE_TANH-LIBM_DD(tanh)-#else-LIBM_DD_NO(tanh)-#endif-#ifdef HAVE_TGAMMA-LIBM_DD(tgamma)-#else-LIBM_DD_NO(tgamma)-#endif-#ifdef HAVE_Y0-LIBM_DD(y0)-#else-LIBM_DD_NO(y0)-#endif-#ifdef HAVE_Y1-LIBM_DD(y1)-#else-LIBM_DD_NO(y1)-#endif-#ifdef HAVE_JN-LIBM_DID(jn)-#endif-#ifdef HAVE_YN-LIBM_DID(yn)-#endif
linker.c
@@ -1,388 +0,0 @@-#include <assert.h>-#include <errno.h>-#include <limits.h>-#include <string.h>-#include <stdio.h>-#include <stdlib.h>-#include <stdint.h>-#include <sys/stat.h>-#include <libgen.h>--#include "jq_parser.h"-#include "locfile.h"-#include "jv.h"-#include "jq.h"-#include "parser.h"-#include "util.h"-#include "compile.h"--struct lib_loading_state {- char **names;- block *defs;- uint64_t ct;-};-static int load_library(jq_state *jq, jv lib_path, int is_data, int raw,- const char *as, block *out_block,- struct lib_loading_state *lib_state);--static int path_is_relative(jv p) {- const char *s = jv_string_value(p);--#ifdef WIN32- int res = PathIsRelativeA(s);-#else- int res = *s != '/';-#endif- jv_free(p);- return res;-}---// Given a lib_path to search first, creates a chain of search paths-// in the following order:-// 1. lib_path-// 2. -L paths passed in on the command line (from jq_state*) or builtin list-static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv lib_origin) {- assert(jv_get_kind(search_path) == JV_KIND_ARRAY);- jv expanded = jv_array();- jv expanded_elt;- jv err = jv_null();- jv_array_foreach(search_path, i, path) {- if (jv_get_kind(path) != JV_KIND_STRING) {- jv_free(path);- continue;- }- path = expand_path(path);- if (!jv_is_valid(path)) {- err = path;- path = jv_null();- continue;- }- if (strcmp(".",jv_string_value(path)) == 0) {- expanded_elt = jv_copy(path);- } else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/") - 1) == 0) {- expanded_elt = jv_string_fmt("%s/%s",- jv_string_value(jq_origin),- jv_string_value(path) + sizeof ("$ORIGIN/") - 1);- } else if (jv_get_kind(lib_origin) == JV_KIND_STRING &&- path_is_relative(jv_copy(path))) {- expanded_elt = jv_string_fmt("%s/%s",- jv_string_value(lib_origin),- jv_string_value(path));- } else {- expanded_elt = path;- path = jv_invalid();- }- expanded = jv_array_append(expanded, expanded_elt);- jv_free(path);- }- jv_free(jq_origin);- jv_free(lib_origin);- jv_free(search_path);- return JV_ARRAY(expanded, err);-}--// Doesn't actually check that name not be an absolute path, and we-// don't have to: we always append relative paths to others (with a '/'-// in between).-static jv validate_relpath(jv name) {- const char *s = jv_string_value(name);- if (strchr(s, '\\')) {- jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s));- jv_free(name);- return res;- }- jv components = jv_string_split(jv_copy(name), jv_string("/"));- jv rp = jv_array_get(jv_copy(components), 0);- components = jv_array_slice(components, 1, jv_array_length(jv_copy(components)));- jv_array_foreach(components, i, x) {- if (!strcmp(jv_string_value(x), "..")) {- jv_free(x);- jv_free(rp);- jv_free(components);- jv res = jv_invalid_with_msg(jv_string_fmt("Relative paths to modules may not traverse to parent directories (%s)", s));- jv_free(name);- return res;- }- if (i > 0 && jv_equal(jv_copy(x), jv_array_get(jv_copy(components), i - 1))) {- jv_free(x);- jv_free(rp);- jv_free(components);- jv res = jv_invalid_with_msg(jv_string_fmt("module names must not have equal consecutive components: %s",- jv_string_value(name)));- jv_free(name);- return res;- }- rp = jv_string_concat(rp, jv_string_concat(jv_string("/"), x));- }- jv_free(components);- jv_free(name);- return rp;-}--// Assumes name has been validated-static jv jv_basename(jv name) {- const char *s = jv_string_value(name);- const char *p = strrchr(s, '/');- if (!p)- return name;- jv res = jv_string_fmt("%s", p);- jv_free(name);- return res;-}--// Asummes validated relative path to module-static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {- if (jv_get_kind(search) != JV_KIND_ARRAY)- return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));- if (jv_get_kind(rel_path) != JV_KIND_STRING)- return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));-- struct stat st;- int ret;-- // Ideally we should cache this somewhere- search = build_lib_search_chain(jq, search, jq_origin, lib_origin);- jv err = jv_array_get(jv_copy(search), 1);- search = jv_array_get(search, 0);-- jv bname = jv_basename(jv_copy(rel_path));-- jv_array_foreach(search, i, spath) {- if (jv_get_kind(spath) == JV_KIND_NULL) {- jv_free(spath);- break;- }- if (jv_get_kind(spath) != JV_KIND_STRING ||- strcmp(jv_string_value(spath), "") == 0) {- jv_free(spath);- continue; /* XXX report non-strings in search path?? */- }- // Try ${search_dir}/${rel_path}.jq- jv testpath = jq_realpath(jv_string_fmt("%s/%s%s",- jv_string_value(spath),- jv_string_value(rel_path),- suffix));- ret = stat(jv_string_value(testpath),&st);- if (ret == -1 && errno == ENOENT) {- jv_free(testpath);- // Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq- testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",- jv_string_value(spath),- jv_string_value(rel_path),- "jq/main",- suffix));- ret = stat(jv_string_value(testpath),&st);- }- if (ret == -1 && errno == ENOENT) {- jv_free(testpath);- // Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq- testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",- jv_string_value(spath),- jv_string_value(rel_path),- jv_string_value(bname),- suffix));- ret = stat(jv_string_value(testpath),&st);- }- if (ret == 0) {- jv_free(err);- jv_free(rel_path);- jv_free(search);- jv_free(bname);- jv_free(spath);- return testpath;- }- jv_free(testpath);- jv_free(spath);- }- jv output;- if (!jv_is_valid(err)) {- err = jv_invalid_get_msg(err);- output = jv_invalid_with_msg(jv_string_fmt("module not found: %s (%s)",- jv_string_value(rel_path),- jv_string_value(err)));- } else {- output = jv_invalid_with_msg(jv_string_fmt("module not found: %s",- jv_string_value(rel_path)));- }- jv_free(err);- jv_free(rel_path);- jv_free(search);- jv_free(bname);- return output;-}--static jv default_search(jq_state *jq, jv value) {- if (!jv_is_valid(value)) {- // dependent didn't say; prepend . to system search path listj- jv_free(value);- return jv_array_concat(JV_ARRAY(jv_string(".")), jq_get_lib_dirs(jq));- }- if (jv_get_kind(value) != JV_KIND_ARRAY)- return JV_ARRAY(value);- return value;-}--// XXX Split this into a util that takes a callback, and then...-static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {- jv deps = block_take_imports(src_block);- block bk = *src_block;- int nerrors = 0;- const char *as_str = NULL;-- jv_array_foreach(deps, i, dep) {- int is_data = jv_get_kind(jv_object_get(jv_copy(dep), jv_string("is_data"))) == JV_KIND_TRUE;- int raw = 0;- jv v = jv_object_get(jv_copy(dep), jv_string("raw"));- if (jv_get_kind(v) == JV_KIND_TRUE)- raw = 1;- jv_free(v);- jv relpath = validate_relpath(jv_object_get(jv_copy(dep), jv_string("relpath")));- jv as = jv_object_get(jv_copy(dep), jv_string("as"));- assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING);- if (jv_get_kind(as) == JV_KIND_STRING)- as_str = jv_string_value(as);- jv search = default_search(jq, jv_object_get(dep, jv_string("search")));- // dep is now freed; do not reuse-- // find_lib does a lot of work that could be cached...- jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(jq_origin), jv_copy(lib_origin));- // XXX ...move the rest of this into a callback.- if (!jv_is_valid(resolved)) {- jv emsg = jv_invalid_get_msg(resolved);- jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg)));- jv_free(emsg);- jv_free(as);- jv_free(deps);- jv_free(jq_origin);- jv_free(lib_origin);- return 1;- }- uint64_t state_idx = 0;- for (; state_idx < lib_state->ct; ++state_idx) {- if (strcmp(lib_state->names[state_idx],jv_string_value(resolved)) == 0)- break;- }- if (state_idx < lib_state->ct) { // Found- jv_free(resolved);- // Bind the library to the program- bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, as_str);- } else { // Not found. Add it to the table before binding.- block dep_def_block = gen_noop();- nerrors += load_library(jq, resolved, is_data, raw, as_str, &dep_def_block, lib_state);- // resolved has been freed- if (nerrors == 0) {- // Bind the library to the program- bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str);- }- }- jv_free(as);- }- jv_free(lib_origin);- jv_free(jq_origin);- jv_free(deps);- return nerrors;-}--// Loads the library at lib_path into lib_state, putting the library's defs-// into *out_block-static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, const char *as, block *out_block, struct lib_loading_state *lib_state) {- int nerrors = 0;- struct locfile* src = NULL;- block program;- jv data;- if (is_data && !raw)- data = jv_load_file(jv_string_value(lib_path), 0);- else- data = jv_load_file(jv_string_value(lib_path), 1);- int state_idx;- if (!jv_is_valid(data)) {- if (jv_invalid_has_msg(jv_copy(data)))- data = jv_invalid_get_msg(data);- else- data = jv_string("unknown error");- jq_report_error(jq, jv_string_fmt("jq: error loading data file %s: %s\n", jv_string_value(lib_path), jv_string_value(data)));- nerrors++;- goto out;- } else if (is_data) {- // import "foo" as $bar;- program = gen_const_global(jv_copy(data), as);- } else {- // import "foo" as bar;- src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data)));- nerrors += jq_parse_library(src, &program);- if (nerrors == 0) {- char *lib_origin = strdup(jv_string_value(lib_path));- nerrors += process_dependencies(jq, jq_get_jq_origin(jq),- jv_string(dirname(lib_origin)),- &program, lib_state);- free(lib_origin);- }- }- state_idx = lib_state->ct++;- lib_state->names = realloc(lib_state->names, lib_state->ct * sizeof(const char *));- lib_state->defs = realloc(lib_state->defs, lib_state->ct * sizeof(block));- lib_state->names[state_idx] = strdup(jv_string_value(lib_path));- lib_state->defs[state_idx] = program;- *out_block = program;- if (src)- locfile_free(src);-out:- jv_free(lib_path);- jv_free(data);- return nerrors;-}--// FIXME It'd be nice to have an option to search the same search path-// as we do in process_dependencies.-jv load_module_meta(jq_state *jq, jv mod_relpath) {- // We can't know the caller's origin; we could though, if it was passed in- jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", jq_get_jq_origin(jq), jv_null());- if (!jv_is_valid(lib_path))- return lib_path;- jv meta = jv_null();- jv data = jv_load_file(jv_string_value(lib_path), 1);- if (jv_is_valid(data)) {- block program;- struct locfile* src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data)));- int nerrors = jq_parse_library(src, &program);- if (nerrors == 0) {- meta = block_module_meta(program);- if (jv_get_kind(meta) == JV_KIND_NULL)- meta = jv_object();- meta = jv_object_set(meta, jv_string("deps"), block_take_imports(&program));- }- locfile_free(src);- block_free(program);- }- jv_free(lib_path);- jv_free(data);- return meta;-}--int load_program(jq_state *jq, struct locfile* src, block *out_block) {- int nerrors = 0;- block program;- struct lib_loading_state lib_state = {0,0,0};- nerrors = jq_parse(src, &program);- if (nerrors)- return nerrors;-- nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);- block libs = gen_noop();- for (uint64_t i = 0; i < lib_state.ct; ++i) {- free(lib_state.names[i]);- if (nerrors == 0 && !block_is_const(lib_state.defs[i]))- libs = block_join(libs, lib_state.defs[i]);- else- block_free(lib_state.defs[i]);- }- free(lib_state.names);- free(lib_state.defs);- if (nerrors)- block_free(program);- else- *out_block = block_drop_unreferenced(block_join(libs, program));-- return nerrors;-}
linker.h
@@ -1,7 +0,0 @@-#ifndef LINKER_H-#define LINKER_H--int load_program(jq_state *jq, struct locfile* src, block *out_block);-jv load_module_meta(jq_state *jq, jv modname);--#endif
locfile.c
@@ -1,91 +0,0 @@-#include <assert.h>-#include <errno.h>-#include <stdarg.h>-#include <stdlib.h>-#include <stdio.h>-#include <string.h>-#include "jq.h"-#include "jv_alloc.h"-#include "locfile.h"---struct locfile* locfile_init(jq_state *jq, const char *fname, const char* data, int length) {- struct locfile* l = jv_mem_alloc(sizeof(struct locfile));- l->jq = jq;- l->fname = jv_string(fname);- l->data = jv_mem_alloc(length);- memcpy((char*)l->data,data,length);- l->length = length;- l->nlines = 1;- l->refct = 1;- for (int i=0; i<length; i++) {- if (data[i] == '\n') l->nlines++;- }- l->linemap = jv_mem_alloc(sizeof(int) * (l->nlines + 1));- l->linemap[0] = 0;- int line = 1;- for (int i=0; i<length; i++) {- if (data[i] == '\n') {- l->linemap[line] = i+1; // at start of line, not of \n- line++;- }- }- l->linemap[l->nlines] = length+1; // virtual last \n- return l;-}--struct locfile* locfile_retain(struct locfile* l) {- l->refct++;- return l;-}-void locfile_free(struct locfile* l) {- if (--(l->refct) == 0) {- jv_free(l->fname);- jv_mem_free(l->linemap);- jv_mem_free((char*)l->data);- jv_mem_free(l);- }-}--int locfile_get_line(struct locfile* l, int pos) {- assert(pos < l->length);- int line = 1;- while (l->linemap[line] <= pos) line++; // == if pos at start (before, never ==, because pos never on \n)- assert(line-1 < l->nlines);- return line-1;-}--static int locfile_line_length(struct locfile* l, int line) {- assert(line < l->nlines);- return l->linemap[line+1] - l->linemap[line] -1; // -1 to omit \n-}--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 at %s, line %d:\n%.*s%*s", jv_string_value(m1),- jv_string_value(l->fname), startline + 1,- locfile_line_length(l, startline), l->data + offset,- loc.start - offset, "");- jv_free(m1);- jq_report_error(l->jq, m2);- return;-}
locfile.h
@@ -1,29 +0,0 @@-#ifndef LOCFILE_H-#define LOCFILE_H--#include "jq.h"--typedef struct {- int start, end;-} location;--static const location UNKNOWN_LOCATION = {-1, -1};--struct locfile {- jv fname;- const char* data;- int length;- int* linemap;- int nlines;- char *error;- jq_state *jq;- int refct;-};--struct locfile* locfile_init(jq_state *, const char *, const char *, int);-struct locfile* locfile_retain(struct locfile *);-int locfile_get_line(struct locfile *, int);-void locfile_free(struct locfile *);-void locfile_locate(struct locfile *, location, const char *, ...);--#endif
main.c
@@ -1,566 +0,0 @@-#include <assert.h>-#include <ctype.h>-#include <errno.h>-#include <libgen.h>-#include <stdlib.h>-#include <stdio.h>-#include <string.h>-#include <unistd.h>--#ifdef WIN32-#include <windows.h>-#include <io.h>-#include <fcntl.h>-#include <processenv.h>-#include <shellapi.h>-#include <wchar.h>-#include <wtypes.h>-#endif--#include "compile.h"-#include "jv.h"-#include "jq.h"-#include "jv_alloc.h"-#include "util.h"-#include "version.h"--int jq_testsuite(jv lib_dirs, int verbose, int argc, char* argv[]);--static const char* progname;--/*- * For a longer help message we could use a better option parsing- * strategy, one that lets stack options.- */-static void usage(int code) {- FILE *f = stderr;-- if (code == 0)- f = stdout;-- int ret = fprintf(f,- "jq - commandline JSON processor [version %s]\n"- "Usage: %s [options] <jq filter> [file...]\n\n"- "\tjq is a tool for processing JSON inputs, applying the\n"- "\tgiven filter to its JSON text inputs and producing the\n"- "\tfilter's results as JSON on standard output.\n"- "\tThe simplest filter is ., which is the identity filter,\n"- "\tcopying jq's input to its output unmodified (except for\n"- "\tformatting).\n"- "\tFor more advanced filters see the jq(1) manpage (\"man jq\")\n"- "\tand/or https://stedolan.github.io/jq\n\n"- "\tSome of the options include:\n"- "\t -c\t\tcompact instead of pretty-printed output;\n"- "\t -n\t\tuse `null` as the single input value;\n"- "\t -e\t\tset the exit status code based on the output;\n"- "\t -s\t\tread (slurp) all inputs into an array; apply filter to it;\n"- "\t -r\t\toutput raw strings, not JSON texts;\n"- "\t -R\t\tread raw strings, not JSON texts;\n"- "\t -C\t\tcolorize JSON;\n"- "\t -M\t\tmonochrome (don't colorize JSON);\n"- "\t -S\t\tsort keys of objects on output;\n"- "\t --tab\tuse tabs for indentation;\n"- "\t --arg a v\tset variable $a to value <v>;\n"- "\t --argjson a v\tset variable $a to JSON value <v>;\n"- "\t --slurpfile a f\tset variable $a to an array of JSON texts read from <f>;\n"- "\tSee the manpage for more options.\n", JQ_VERSION, progname);- exit((ret < 0 && code == 0) ? 2 : code);-}--static void die() {- fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);- fprintf(stderr, "or see the jq manpage, or online docs at https://stedolan.github.io/jq\n");- exit(2);-}-----static int isoptish(const char* text) {- return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));-}--static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {- if (text[0] != '-' || text[1] == '-')- *short_opts = 0;- if (text[0] != '-') return 0;-- // check long option- if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;- else if (text[1] == '-') return 0;-- // must be short option; check it and...- if (!shortopt) return 0;- if (strchr(text, shortopt) != NULL) {- (*short_opts)++; // ...count it (for option stacking)- return 1;- }- return 0;-}--enum {- SLURP = 1,- RAW_INPUT = 2,- PROVIDE_NULL = 4,- RAW_OUTPUT = 8,- ASCII_OUTPUT = 32,- COLOR_OUTPUT = 64,- NO_COLOR_OUTPUT = 128,- SORTED_OUTPUT = 256,- FROM_FILE = 512,- RAW_NO_LF = 1024,- UNBUFFERED_OUTPUT = 2048,- EXIT_STATUS = 4096,- SEQ = 8192,- RUN_TESTS = 16384,- /* debugging only */- DUMP_DISASM = 32768,-};-static int options = 0;--static int process(jq_state *jq, jv value, int flags, int dumpopts) {- 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(result, dumpopts);- }- 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 debug_cb(void *data, jv input) {- int dumpopts = *(int *)data;- jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY));- fprintf(stderr, "\n");-}--int main(int argc, char* argv[]) {- jq_state *jq = NULL;- int ret = 0;- int compiled = 0;- int parser_flags = 0;- int nfiles = 0;- int badwrite;- jv program_arguments = jv_array();--#ifdef WIN32- SetConsoleOutputCP(CP_UTF8);- fflush(stdout);- fflush(stderr);- _setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);- _setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);- int wargc;- wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);- assert(wargc == argc);- size_t arg_sz;- for (int i = 0; i < argc; i++) {- argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8,- 0,- wargv[i],- -1, 0, 0, 0, 0)));- WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0);- }-#endif-- if (argc) progname = argv[0];-- jq = jq_init();- if (jq == NULL) {- perror("malloc");- ret = 2;- goto out;- }-- int dumpopts = JV_PRINT_INDENT_FLAGS(2);- const char* program = 0;-- jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb-- int further_args_are_files = 0;- int jq_flags = 0;- size_t short_opts = 0;- jv lib_search_paths = jv_null();- for (int i=1; i<argc; i++, short_opts = 0) {- if (further_args_are_files) {- jq_util_input_add_input(input_state, argv[i]);- nfiles++;- } else if (!strcmp(argv[i], "--")) {- if (!program) usage(2);- further_args_are_files = 1;- } else if (!isoptish(argv[i])) {- if (program) {- jq_util_input_add_input(input_state, argv[i]);- nfiles++;- } else {- program = argv[i];- }- } else {- if (argv[i][1] == 'L') {- if (jv_get_kind(lib_search_paths) == JV_KIND_NULL)- lib_search_paths = jv_array();- if (argv[i][2] != 0) { // -Lname (faster check than strlen)- lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i]+2)));- } else if (i >= argc - 1) {- fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n");- die();- } else {- lib_search_paths = jv_array_append(lib_search_paths, jq_realpath(jv_string(argv[i+1])));- i++;- }- continue;- }-- if (isoption(argv[i], 's', "slurp", &short_opts)) {- options |= SLURP;- if (!short_opts) continue;- }- if (isoption(argv[i], 'r', "raw-output", &short_opts)) {- options |= RAW_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'c', "compact-output", &short_opts)) {- dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));- if (!short_opts) continue;- }- if (isoption(argv[i], 'C', "color-output", &short_opts)) {- options |= COLOR_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) {- options |= NO_COLOR_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'a', "ascii-output", &short_opts)) {- options |= ASCII_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "unbuffered", &short_opts)) {- options |= UNBUFFERED_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {- options |= SORTED_OUTPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'R', "raw-input", &short_opts)) {- options |= RAW_INPUT;- if (!short_opts) continue;- }- if (isoption(argv[i], 'n', "null-input", &short_opts)) {- options |= PROVIDE_NULL;- if (!short_opts) continue;- }- if (isoption(argv[i], 'f', "from-file", &short_opts)) {- options |= FROM_FILE;- if (!short_opts) continue;- }- if (isoption(argv[i], 'j', "join-output", &short_opts)) {- options |= RAW_OUTPUT | RAW_NO_LF;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "tab", &short_opts)) {- dumpopts &= ~JV_PRINT_INDENT_FLAGS(7);- dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "indent", &short_opts)) {- if (i >= argc - 1) {- fprintf(stderr, "%s: --indent takes one parameter\n", progname);- die();- }- dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));- int indent = atoi(argv[i+1]);- if (indent < -1 || indent > 7) {- fprintf(stderr, "%s: --indent takes a number between -1 and 7\n", progname);- die();- }- dumpopts |= JV_PRINT_INDENT_FLAGS(indent);- i++;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "seq", &short_opts)) {- options |= SEQ;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "stream", &short_opts)) {- parser_flags |= JV_PARSE_STREAMING;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "stream-errors", &short_opts)) {- parser_flags |= JV_PARSE_STREAM_ERRORS;- if (!short_opts) continue;- }- if (isoption(argv[i], 'e', "exit-status", &short_opts)) {- options |= EXIT_STATUS;- if (!short_opts) continue;- }- // FIXME: For --arg* we should check that the varname is acceptable- if (isoption(argv[i], 0, "arg", &short_opts)) {- if (i >= argc - 2) {- fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname);- die();- }- jv arg = jv_object();- arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));- arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));- program_arguments = jv_array_append(program_arguments, arg);- i += 2; // skip the next two arguments- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "argjson", &short_opts)) {- if (i >= argc - 2) {- fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname);- die();- }- jv v = jv_parse(argv[i+2]);- if (!jv_is_valid(v)) {- fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);- die();- }- jv arg = jv_object();- arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));- arg = jv_object_set(arg, jv_string("value"), v);- program_arguments = jv_array_append(program_arguments, arg);- i += 2; // skip the next two arguments- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "argfile", &short_opts) ||- isoption(argv[i], 0, "slurpfile", &short_opts)) {- const char *which;- if (isoption(argv[i], 0, "argfile", &short_opts))- which = "argfile";- else- which = "slurpfile";- if (i >= argc - 2) {- fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which);- die();- }- jv arg = jv_object();- arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));- jv data = jv_load_file(argv[i+2], 0);- if (!jv_is_valid(data)) {- data = jv_invalid_get_msg(data);- fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,- argv[i+1], argv[i+2], jv_string_value(data));- jv_free(data);- jv_free(arg);- ret = 2;- goto out;- }- if (isoption(argv[i], 0, "argfile", &short_opts) &&- jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)- data = jv_array_get(data, 0);- arg = jv_object_set(arg, jv_string("value"), data);- program_arguments = jv_array_append(program_arguments, arg);- i += 2; // skip the next two arguments- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {- options |= DUMP_DISASM;- if (!short_opts) continue;- }- if (isoption(argv[i], 0, "debug-trace", &short_opts)) {- jq_flags |= JQ_DEBUG_TRACE;- if (!short_opts) continue;- }- if (isoption(argv[i], 'h', "help", &short_opts)) {- usage(0);- if (!short_opts) continue;- }- if (isoption(argv[i], 'V', "version", &short_opts)) {- printf("jq-%s\n", JQ_VERSION);- ret = 0;- goto out;- }- if (isoption(argv[i], 0, "run-tests", &short_opts)) {- i++;- // XXX Pass program_arguments, even a whole jq_state *, through;- // could be useful for testing- ret = jq_testsuite(lib_search_paths,- (options & DUMP_DISASM) || (jq_flags & JQ_DEBUG_TRACE),- argc - i, argv + i);- goto out;- }-- // check for unknown options... if this argument was a short option- if (strlen(argv[i]) != short_opts + 1) {- fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);- die();- }- }- }-- if (isatty(fileno(stdout))) {- dumpopts |= JV_PRINT_ISATTY;-#ifndef WIN32- /* Disable color by default on Windows builds as Windows- terminals tend not to display it correctly */- dumpopts |= JV_PRINT_COLOR;-#endif- }- if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;- if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;- if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR;- if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR;-- if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {- // Default search path list- lib_search_paths = JV_ARRAY(jv_string("~/.jq"),- jv_string("$ORIGIN/../lib/jq"),- jv_string("$ORIGIN/lib"));- }- jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_search_paths);-- char *origin = strdup(argv[0]);- if (origin == NULL) {- fprintf(stderr, "Error: out of memory\n");- exit(1);- }- jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string(dirname(origin)));- free(origin);-- if (strchr(JQ_VERSION, '-') == NULL)- jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION));- else- jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string_fmt("%.*s-master", (int)(strchr(JQ_VERSION, '-') - JQ_VERSION), JQ_VERSION));--#if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY)--#if defined(HAVE__ISATTY) && defined(isatty)-#undef isatty-#define isatty _isatty-#endif-- if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO))- program = ".";-#endif-- if (!program) usage(2);-- if (options & FROM_FILE) {- char *program_origin = strdup(program);- if (program_origin == NULL) {- perror("malloc");- exit(2);- }-- jv data = jv_load_file(program, 1);- if (!jv_is_valid(data)) {- data = jv_invalid_get_msg(data);- fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));- jv_free(data);- ret = 2;- goto out;- }- jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));- compiled = jq_compile_args(jq, jv_string_value(data), jv_copy(program_arguments));- free(program_origin);- jv_free(data);- } else {- jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?- compiled = jq_compile_args(jq, program, jv_copy(program_arguments));- }- if (!compiled){- ret = 3;- goto out;- }-- if (options & DUMP_DISASM) {- jq_dump_disassembly(jq, 0);- printf("\n");- }-- if ((options & SEQ))- parser_flags |= JV_PARSE_SEQ;-- if ((options & RAW_INPUT))- jq_util_input_set_parser(input_state, NULL, (options & SLURP) ? 1 : 0);- else- jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0);-- // Let jq program read from inputs- jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state);-- // Let jq program call `debug` builtin and have that go somewhere- jq_set_debug_cb(jq, debug_cb, &dumpopts);-- if (nfiles == 0)- jq_util_input_add_input(input_state, "-");-- if (options & PROVIDE_NULL) {- ret = process(jq, jv_null(), jq_flags, dumpopts);- } else {- jv value;- while (jq_util_input_errors(input_state) == 0 &&- (jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) {- if (jv_is_valid(value)) {- ret = process(jq, value, jq_flags, dumpopts);- continue;- }-- // Parse error- jv msg = jv_invalid_get_msg(value);- if (!(options & SEQ)) {- // --seq -> errors are not fatal- ret = 4;- fprintf(stderr, "parse error: %s\n", jv_string_value(msg));- jv_free(msg);- break;- }- fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg));- jv_free(msg);- }- }-- if (jq_util_input_errors(input_state) != 0)- ret = 2;--out:- badwrite = ferror(stdout);- if (fclose(stdout)!=0 || badwrite) {- fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno));- ret = 2;- }-- jv_free(program_arguments);- jq_util_input_free(&input_state);- jq_teardown(&jq);- if (ret >= 10 && (options & EXIT_STATUS))- return ret - 10;- if (ret >= 10)- return 0;- return ret;-}
opcode_list.h
@@ -1,44 +0,0 @@-OP(LOADK, CONSTANT, 1, 1)-OP(DUP, NONE, 1, 2)-OP(DUPN, NONE, 1, 2)-OP(DUP2, NONE, 2, 3)-OP(POP, NONE, 1, 0)-OP(LOADV, VARIABLE, 1, 1)-OP(LOADVN, VARIABLE, 1, 1)-OP(STOREV, VARIABLE, 1, 0)-OP(STORE_GLOBAL, GLOBAL, 0, 0)-OP(INDEX, NONE, 2, 1)-OP(INDEX_OPT, NONE, 2, 1)-OP(EACH, NONE, 1, 1)-OP(EACH_OPT, NONE, 1, 1)-OP(FORK, BRANCH, 0, 0)-OP(FORK_OPT, BRANCH, 0, 0)-OP(JUMP, BRANCH, 0, 0)-OP(JUMP_F,BRANCH, 1, 0)-OP(BACKTRACK, NONE, 0, 0)-OP(APPEND, VARIABLE,1, 0)-OP(INSERT, NONE, 4, 2)-OP(RANGE, VARIABLE, 1, 1)--OP(SUBEXP_BEGIN, NONE, 1, 2)-OP(SUBEXP_END, NONE, 2, 2)--OP(PATH_BEGIN, NONE, 1, 2)-OP(PATH_END, NONE, 2, 1)--OP(CALL_BUILTIN, CFUNC, -1, 1)--OP(CALL_JQ, UFUNC, 1, 1)-OP(RET, NONE, 1, 1)-OP(TAIL_CALL_JQ, UFUNC, 1, 1)--OP(CLOSURE_PARAM, DEFINITION, 0, 0)-OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0)-OP(CLOSURE_CREATE, DEFINITION, 0, 0)-OP(CLOSURE_CREATE_C, DEFINITION, 0, 0)--OP(TOP, NONE, 0, 0)-OP(CLOSURE_PARAM_REGULAR, DEFINITION, 0, 0)-OP(DEPS, CONSTANT, 0, 0)-OP(MODULEMETA, CONSTANT, 0, 0)-OP(GENLABEL, NONE, 0, 1)
parser.c
@@ -1,3893 +0,0 @@-/* A Bison parser, made by GNU Bison 3.0.2. */--/* Bison implementation for Yacc-like parsers in C-- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>. */--/* As a special exception, you may create a larger work that contains- part or all of the Bison parser skeleton and distribute that work- under terms of your choice, so long as that work isn't itself a- parser generator using the skeleton or a modified version thereof- as a parser skeleton. Alternatively, if you modify or redistribute- the parser skeleton itself, you may (at your option) remove this- special exception, which will cause the skeleton and the resulting- Bison output files to be licensed under the GNU General Public- License without this special exception.-- This special exception was added by the Free Software Foundation in- version 2.2 of Bison. */--/* C LALR(1) parser skeleton written by Richard Stallman, by- simplifying the original so-called "semantic" parser. */--/* All symbols defined below should begin with yy or YY, to avoid- infringing on user name space. This should be done even for local- variables, as they might otherwise be expanded by user macros.- There are some unavoidable exceptions within include files to- define necessary library symbols; they are noted "INFRINGES ON- USER NAME SPACE" below. */--/* Identify Bison output. */-#define YYBISON 1--/* Bison version. */-#define YYBISON_VERSION "3.0.2"--/* Skeleton name. */-#define YYSKELETON_NAME "yacc.c"--/* Pure parsers. */-#define YYPURE 1--/* Push parsers. */-#define YYPUSH 0--/* Pull parsers. */-#define YYPULL 1-----/* Copy the first part of user declarations. */-#line 1 "parser.y" /* yacc.c:339 */--#include <assert.h>-#include <math.h>-#include <stdio.h>-#include <string.h>-#include "compile.h"-#include "jv_alloc.h"-#define YYMALLOC jv_mem_alloc-#define YYFREE jv_mem_free--#line 77 "parser.c" /* yacc.c:339 */--# ifndef YY_NULLPTR-# if defined __cplusplus && 201103L <= __cplusplus-# define YY_NULLPTR nullptr-# else-# define YY_NULLPTR 0-# endif-# endif--/* Enabling verbose error messages. */-#ifdef YYERROR_VERBOSE-# undef YYERROR_VERBOSE-# define YYERROR_VERBOSE 1-#else-# define YYERROR_VERBOSE 1-#endif--/* In a future release of Bison, this section will be replaced- by #include "y.tab.h". */-#ifndef YY_YY_PARSER_H_INCLUDED-# define YY_YY_PARSER_H_INCLUDED-/* Debug traces. */-#ifndef YYDEBUG-# define YYDEBUG 0-#endif-#if YYDEBUG-extern int yydebug;-#endif-/* "%code requires" blocks. */-#line 11 "parser.y" /* yacc.c:355 */--#include "locfile.h"-struct lexer_param;--#define YYLTYPE location-#define YYLLOC_DEFAULT(Loc, Rhs, N) \- do { \- if (N) { \- (Loc).start = YYRHSLOC(Rhs, 1).start; \- (Loc).end = YYRHSLOC(Rhs, N).end; \- } else { \- (Loc).start = YYRHSLOC(Rhs, 0).end; \- (Loc).end = YYRHSLOC(Rhs, 0).end; \- } \- } while (0)--#line 124 "parser.c" /* yacc.c:355 */--/* Token type. */-#ifndef YYTOKENTYPE-# define YYTOKENTYPE- enum yytokentype- {- INVALID_CHARACTER = 258,- IDENT = 259,- FIELD = 260,- LITERAL = 261,- FORMAT = 262,- REC = 263,- SETMOD = 264,- EQ = 265,- NEQ = 266,- DEFINEDOR = 267,- AS = 268,- DEF = 269,- MODULE = 270,- IMPORT = 271,- INCLUDE = 272,- IF = 273,- THEN = 274,- ELSE = 275,- ELSE_IF = 276,- REDUCE = 277,- FOREACH = 278,- END = 279,- AND = 280,- OR = 281,- TRY = 282,- CATCH = 283,- LABEL = 284,- BREAK = 285,- LOC = 286,- SETPIPE = 287,- SETPLUS = 288,- SETMINUS = 289,- SETMULT = 290,- SETDIV = 291,- SETDEFINEDOR = 292,- LESSEQ = 293,- GREATEREQ = 294,- QQSTRING_START = 295,- QQSTRING_TEXT = 296,- QQSTRING_INTERP_START = 297,- QQSTRING_INTERP_END = 298,- QQSTRING_END = 299,- FUNCDEF = 300,- NONOPT = 301- };-#endif-/* Tokens. */-#define INVALID_CHARACTER 258-#define IDENT 259-#define FIELD 260-#define LITERAL 261-#define FORMAT 262-#define REC 263-#define SETMOD 264-#define EQ 265-#define NEQ 266-#define DEFINEDOR 267-#define AS 268-#define DEF 269-#define MODULE 270-#define IMPORT 271-#define INCLUDE 272-#define IF 273-#define THEN 274-#define ELSE 275-#define ELSE_IF 276-#define REDUCE 277-#define FOREACH 278-#define END 279-#define AND 280-#define OR 281-#define TRY 282-#define CATCH 283-#define LABEL 284-#define BREAK 285-#define LOC 286-#define SETPIPE 287-#define SETPLUS 288-#define SETMINUS 289-#define SETMULT 290-#define SETDIV 291-#define SETDEFINEDOR 292-#define LESSEQ 293-#define GREATEREQ 294-#define QQSTRING_START 295-#define QQSTRING_TEXT 296-#define QQSTRING_INTERP_START 297-#define QQSTRING_INTERP_END 298-#define QQSTRING_END 299-#define FUNCDEF 300-#define NONOPT 301--/* Value type. */-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED-typedef union YYSTYPE YYSTYPE;-union YYSTYPE-{-#line 31 "parser.y" /* yacc.c:355 */-- jv literal;- block blk;--#line 233 "parser.c" /* yacc.c:355 */-};-# define YYSTYPE_IS_TRIVIAL 1-# define YYSTYPE_IS_DECLARED 1-#endif--/* Location type. */-#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED-typedef struct YYLTYPE YYLTYPE;-struct YYLTYPE-{- int first_line;- int first_column;- int last_line;- int last_column;-};-# define YYLTYPE_IS_DECLARED 1-# define YYLTYPE_IS_TRIVIAL 1-#endif----int yyparse (block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr);--#endif /* !YY_YY_PARSER_H_INCLUDED */--/* Copy the second part of user declarations. */-#line 123 "parser.y" /* yacc.c:358 */--#include "lexer.h"-struct lexer_param {- yyscan_t lexer;-};-#define FAIL(loc, msg) \- do { \- location l = loc; \- yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \- /*YYERROR*/; \- } while (0)--void yyerror(YYLTYPE* loc, block* answer, int* errors,- struct locfile* locations, struct lexer_param* lexer_param_ptr, const char *s){- (*errors)++;- if (strstr(s, "unexpected")) {-#ifdef WIN32- locfile_locate(locations, *loc, "jq: error: %s (Windows cmd shell quoting issues?)", s);-#else- locfile_locate(locations, *loc, "jq: error: %s (Unix shell quoting issues?)", s);-#endif- } else {- locfile_locate(locations, *loc, "jq: error: %s", s);- }-}--int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,- struct locfile* locations, struct lexer_param* lexer_param_ptr) {- yyscan_t lexer = lexer_param_ptr->lexer;- int tok = jq_yylex(yylval, yylloc, lexer);- if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {- jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));- if (jv_get_kind(msg) == JV_KIND_STRING) {- FAIL(*yylloc, jv_string_value(msg));- } else {- FAIL(*yylloc, "Invalid literal");- }- jv_free(msg);- jv_free(yylval->literal);- yylval->literal = jv_null();- }- return tok;-}--static block gen_dictpair(block k, block v) {- return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT));-}--static block gen_index(block obj, block key) {- return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));-}--static block gen_index_opt(block obj, block key) {- return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT));-}--static block gen_slice_index(block obj, block start, block end, opcode idx_op) {- block key = BLOCK(gen_subexp(gen_const(jv_object())),- gen_subexp(gen_const(jv_string("start"))),- gen_subexp(start),- gen_op_simple(INSERT),- gen_subexp(gen_const(jv_string("end"))),- gen_subexp(end),- gen_op_simple(INSERT));- return BLOCK(key, obj, gen_op_simple(idx_op));-}--static block constant_fold(block a, block b, int op) {- if (!block_is_single(a) || !block_is_const(a) ||- !block_is_single(b) || !block_is_const(b))- return gen_noop();- if (op == '+') {- if (block_const_kind(a) == JV_KIND_NULL) {- block_free(a);- return b;- }- if (block_const_kind(b) == JV_KIND_NULL) {- block_free(b);- return a;- }- }- if (block_const_kind(a) != block_const_kind(b))- return gen_noop();-- jv res = jv_invalid();-- if (block_const_kind(a) == JV_KIND_NUMBER) {- double na = jv_number_value(block_const(a));- double nb = jv_number_value(block_const(b));- switch (op) {- case '+': res = jv_number(na + nb); break;- case '-': res = jv_number(na - nb); break;- case '*': res = jv_number(na * nb); break;- case '/': res = jv_number(na / nb); break;- case EQ: res = (na == nb ? jv_true() : jv_false()); break;- case NEQ: res = (na != nb ? jv_true() : jv_false()); break;- case '<': res = (na < nb ? jv_true() : jv_false()); break;- case '>': res = (na > nb ? jv_true() : jv_false()); break;- case LESSEQ: res = (na <= nb ? jv_true() : jv_false()); break;- case GREATEREQ: res = (na >= nb ? jv_true() : jv_false()); break;- default: break;- }- } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) {- res = jv_string_concat(block_const(a), block_const(b));- } else {- return gen_noop();- }-- if (jv_get_kind(res) == JV_KIND_INVALID)- return gen_noop();-- block_free(a);- block_free(b);- return gen_const(res);-}--static block gen_binop(block a, block b, int op) {- block folded = constant_fold(a, b, op);- if (!block_is_noop(folded))- return folded;-- const char* funcname = 0;- switch (op) {- case '+': funcname = "_plus"; break;- case '-': funcname = "_minus"; break;- case '*': funcname = "_multiply"; break;- case '/': funcname = "_divide"; break;- case '%': funcname = "_mod"; break;- case EQ: funcname = "_equal"; break;- case NEQ: funcname = "_notequal"; break;- case '<': funcname = "_less"; break;- case '>': funcname = "_greater"; break;- case LESSEQ: funcname = "_lesseq"; break;- case GREATEREQ: funcname = "_greatereq"; break;- }- assert(funcname);-- return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b)));-}--static block gen_format(block a, jv fmt) {- return BLOCK(a, gen_call("format", gen_lambda(gen_const(fmt))));-}--static block gen_definedor_assign(block object, block val) {- block tmp = gen_op_var_fresh(STOREV, "tmp");- return BLOCK(gen_op_simple(DUP),- val, tmp,- gen_call("_modify", BLOCK(gen_lambda(object),- gen_lambda(gen_definedor(gen_noop(),- gen_op_bound(LOADV, tmp))))));-}--static block gen_update(block object, block val, int optype) {- block tmp = gen_op_var_fresh(STOREV, "tmp");- return BLOCK(gen_op_simple(DUP),- val,- tmp,- gen_call("_modify", BLOCK(gen_lambda(object),- gen_lambda(gen_binop(gen_noop(),- gen_op_bound(LOADV, tmp),- optype)))));-}---#line 426 "parser.c" /* yacc.c:358 */--#ifdef short-# undef short-#endif--#ifdef YYTYPE_UINT8-typedef YYTYPE_UINT8 yytype_uint8;-#else-typedef unsigned char yytype_uint8;-#endif--#ifdef YYTYPE_INT8-typedef YYTYPE_INT8 yytype_int8;-#else-typedef signed char yytype_int8;-#endif--#ifdef YYTYPE_UINT16-typedef YYTYPE_UINT16 yytype_uint16;-#else-typedef unsigned short int yytype_uint16;-#endif--#ifdef YYTYPE_INT16-typedef YYTYPE_INT16 yytype_int16;-#else-typedef short int yytype_int16;-#endif--#ifndef YYSIZE_T-# ifdef __SIZE_TYPE__-# define YYSIZE_T __SIZE_TYPE__-# elif defined size_t-# define YYSIZE_T size_t-# elif ! defined YYSIZE_T-# include <stddef.h> /* INFRINGES ON USER NAME SPACE */-# define YYSIZE_T size_t-# else-# define YYSIZE_T unsigned int-# endif-#endif--#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)--#ifndef YY_-# if defined YYENABLE_NLS && YYENABLE_NLS-# if ENABLE_NLS-# include <libintl.h> /* INFRINGES ON USER NAME SPACE */-# define YY_(Msgid) dgettext ("bison-runtime", Msgid)-# endif-# endif-# ifndef YY_-# define YY_(Msgid) Msgid-# endif-#endif--#ifndef YY_ATTRIBUTE-# if (defined __GNUC__ \- && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \- || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C-# define YY_ATTRIBUTE(Spec) __attribute__(Spec)-# else-# define YY_ATTRIBUTE(Spec) /* empty */-# endif-#endif--#ifndef YY_ATTRIBUTE_PURE-# define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__))-#endif--#ifndef YY_ATTRIBUTE_UNUSED-# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))-#endif--#if !defined _Noreturn \- && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)-# if defined _MSC_VER && 1200 <= _MSC_VER-# define _Noreturn __declspec (noreturn)-# else-# define _Noreturn YY_ATTRIBUTE ((__noreturn__))-# endif-#endif--/* Suppress unused-variable warnings by "using" E. */-#if ! defined lint || defined __GNUC__-# define YYUSE(E) ((void) (E))-#else-# define YYUSE(E) /* empty */-#endif--#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__-/* Suppress an incorrect diagnostic about yylval being uninitialized. */-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \- _Pragma ("GCC diagnostic push") \- _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\- _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")-# define YY_IGNORE_MAYBE_UNINITIALIZED_END \- _Pragma ("GCC diagnostic pop")-#else-# define YY_INITIAL_VALUE(Value) Value-#endif-#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN-# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN-# define YY_IGNORE_MAYBE_UNINITIALIZED_END-#endif-#ifndef YY_INITIAL_VALUE-# define YY_INITIAL_VALUE(Value) /* Nothing. */-#endif---#if ! defined yyoverflow || YYERROR_VERBOSE--/* The parser invokes alloca or malloc; define the necessary symbols. */--# ifdef YYSTACK_USE_ALLOCA-# if YYSTACK_USE_ALLOCA-# ifdef __GNUC__-# define YYSTACK_ALLOC __builtin_alloca-# elif defined __BUILTIN_VA_ARG_INCR-# include <alloca.h> /* INFRINGES ON USER NAME SPACE */-# elif defined _AIX-# define YYSTACK_ALLOC __alloca-# elif defined _MSC_VER-# include <malloc.h> /* INFRINGES ON USER NAME SPACE */-# define alloca _alloca-# else-# define YYSTACK_ALLOC alloca-# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */- /* Use EXIT_SUCCESS as a witness for stdlib.h. */-# ifndef EXIT_SUCCESS-# define EXIT_SUCCESS 0-# endif-# endif-# endif-# endif-# endif--# ifdef YYSTACK_ALLOC- /* Pacify GCC's 'empty if-body' warning. */-# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)-# ifndef YYSTACK_ALLOC_MAXIMUM- /* The OS might guarantee only one guard page at the bottom of the stack,- and a page size can be as small as 4096 bytes. So we cannot safely- invoke alloca (N) if N exceeds 4096. Use a slightly smaller number- to allow for a few compiler-allocated temporary stack slots. */-# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */-# endif-# else-# define YYSTACK_ALLOC YYMALLOC-# define YYSTACK_FREE YYFREE-# ifndef YYSTACK_ALLOC_MAXIMUM-# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM-# endif-# if (defined __cplusplus && ! defined EXIT_SUCCESS \- && ! ((defined YYMALLOC || defined malloc) \- && (defined YYFREE || defined free)))-# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */-# ifndef EXIT_SUCCESS-# define EXIT_SUCCESS 0-# endif-# endif-# ifndef YYMALLOC-# define YYMALLOC malloc-# if ! defined malloc && ! defined EXIT_SUCCESS-void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */-# endif-# endif-# ifndef YYFREE-# define YYFREE free-# if ! defined free && ! defined EXIT_SUCCESS-void free (void *); /* INFRINGES ON USER NAME SPACE */-# endif-# endif-# endif-#endif /* ! defined yyoverflow || YYERROR_VERBOSE */---#if (! defined yyoverflow \- && (! defined __cplusplus \- || (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \- && defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))--/* A type that is properly aligned for any stack member. */-union yyalloc-{- yytype_int16 yyss_alloc;- YYSTYPE yyvs_alloc;- YYLTYPE yyls_alloc;-};--/* The size of the maximum gap between one aligned stack and the next. */-# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)--/* The size of an array large to enough to hold all stacks, each with- N elements. */-# define YYSTACK_BYTES(N) \- ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \- + 2 * YYSTACK_GAP_MAXIMUM)--# define YYCOPY_NEEDED 1--/* Relocate STACK from its old location to the new one. The- local variables YYSIZE and YYSTACKSIZE give the old and new number of- elements in the stack, and YYPTR gives the new location of the- stack. Advance YYPTR to a properly aligned location for the next- stack. */-# define YYSTACK_RELOCATE(Stack_alloc, Stack) \- do \- { \- YYSIZE_T yynewbytes; \- YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \- Stack = &yyptr->Stack_alloc; \- yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \- yyptr += yynewbytes / sizeof (*yyptr); \- } \- while (0)--#endif--#if defined YYCOPY_NEEDED && YYCOPY_NEEDED-/* Copy COUNT objects from SRC to DST. The source and destination do- not overlap. */-# ifndef YYCOPY-# if defined __GNUC__ && 1 < __GNUC__-# define YYCOPY(Dst, Src, Count) \- __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))-# else-# define YYCOPY(Dst, Src, Count) \- do \- { \- YYSIZE_T yyi; \- for (yyi = 0; yyi < (Count); yyi++) \- (Dst)[yyi] = (Src)[yyi]; \- } \- while (0)-# endif-# endif-#endif /* !YYCOPY_NEEDED */--/* YYFINAL -- State number of the termination state. */-#define YYFINAL 27-/* YYLAST -- Last index in YYTABLE. */-#define YYLAST 1938--/* YYNTOKENS -- Number of terminals. */-#define YYNTOKENS 68-/* YYNNTS -- Number of nonterminals. */-#define YYNNTS 28-/* YYNRULES -- Number of rules. */-#define YYNRULES 156-/* YYNSTATES -- Number of states. */-#define YYNSTATES 306--/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned- by yylex, with out-of-bounds checking. */-#define YYUNDEFTOK 2-#define YYMAXUTOK 301--#define YYTRANSLATE(YYX) \- ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)--/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM- as returned by yylex, without out-of-bounds checking. */-static const yytype_uint8 yytranslate[] =-{- 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 61, 55, 2, 2,- 59, 60, 53, 51, 47, 52, 63, 54, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 62, 58,- 49, 48, 50, 57, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 64, 2, 65, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 66, 46, 67, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,- 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,- 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,- 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,- 45, 56-};--#if YYDEBUG- /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */-static const yytype_uint16 yyrline[] =-{- 0, 291, 291, 294, 299, 302, 313, 316, 321, 324,- 329, 333, 337, 341, 345, 349, 352, 357, 361, 365,- 370, 377, 381, 385, 389, 393, 397, 401, 405, 409,- 413, 417, 421, 425, 429, 433, 437, 441, 447, 453,- 457, 461, 465, 469, 473, 477, 481, 485, 490, 493,- 510, 519, 526, 534, 545, 550, 556, 559, 564, 569,- 576, 576, 580, 580, 587, 590, 593, 599, 602, 607,- 610, 613, 619, 622, 625, 633, 637, 640, 643, 646,- 649, 652, 655, 658, 661, 665, 671, 674, 677, 680,- 683, 686, 689, 692, 695, 698, 701, 704, 707, 710,- 713, 716, 719, 726, 730, 734, 746, 751, 752, 753,- 754, 757, 760, 765, 770, 774, 777, 782, 785, 790,- 793, 798, 801, 804, 807, 810, 815, 818, 821, 824,- 827, 830, 833, 836, 839, 842, 845, 848, 851, 854,- 857, 860, 863, 866, 869, 874, 877, 878, 879, 882,- 885, 888, 891, 895, 899, 903, 906-};-#endif--#if YYDEBUG || YYERROR_VERBOSE || 1-/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.- First, the terminals, then, starting at YYNTOKENS, nonterminals. */-static const char *const yytname[] =-{- "$end", "error", "$undefined", "INVALID_CHARACTER", "IDENT", "FIELD",- "LITERAL", "FORMAT", "\"..\"", "\"%=\"", "\"==\"", "\"!=\"", "\"//\"",- "\"as\"", "\"def\"", "\"module\"", "\"import\"", "\"include\"", "\"if\"",- "\"then\"", "\"else\"", "\"elif\"", "\"reduce\"", "\"foreach\"",- "\"end\"", "\"and\"", "\"or\"", "\"try\"", "\"catch\"", "\"label\"",- "\"break\"", "\"__loc__\"", "\"|=\"", "\"+=\"", "\"-=\"", "\"*=\"",- "\"/=\"", "\"//=\"", "\"<=\"", "\">=\"", "QQSTRING_START",- "QQSTRING_TEXT", "QQSTRING_INTERP_START", "QQSTRING_INTERP_END",- "QQSTRING_END", "FUNCDEF", "'|'", "','", "'='", "'<'", "'>'", "'+'",- "'-'", "'*'", "'/'", "'%'", "NONOPT", "'?'", "';'", "'('", "')'", "'$'",- "':'", "'.'", "'['", "']'", "'{'", "'}'", "$accept", "TopLevel",- "Module", "Imports", "FuncDefs", "Exp", "Import", "ImportWhat",- "ImportFrom", "FuncDef", "Params", "Param", "String", "@1", "@2",- "QQString", "ElseBody", "ExpD", "Term", "Args", "Arg", "Pattern",- "ArrayPats", "ObjPats", "ObjPat", "Keyword", "MkDict", "MkDictPair", YY_NULLPTR-};-#endif--# ifdef YYPRINT-/* YYTOKNUM[NUM] -- (External) token number corresponding to the- (internal) symbol number NUM (which must be that of a token). */-static const yytype_uint16 yytoknum[] =-{- 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,- 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,- 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,- 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,- 295, 296, 297, 298, 299, 300, 124, 44, 61, 60,- 62, 43, 45, 42, 47, 37, 301, 63, 59, 40,- 41, 36, 58, 46, 91, 93, 123, 125-};-# endif--#define YYPACT_NINF -180--#define yypact_value_is_default(Yystate) \- (!!((Yystate) == (-180)))--#define YYTABLE_NINF -146--#define yytable_value_is_error(Yytable_value) \- (!!((Yytable_value) == (-146)))-- /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing- STATE-NUM. */-static const yytype_int16 yypact[] =-{- 27, 736, 13, 51, -5, -8, -180, 23, -180, 67,- 736, 779, 779, 736, 15, 3, -180, 736, 464, 20,- 274, 437, 339, 1282, 736, -180, 6, -180, 8, 8,- 736, 51, 646, 736, -180, -180, -7, 1631, 9, 52,- 46, 74, -180, 75, -180, -10, 22, 1117, -180, -180,- -180, 109, 23, 55, 49, -180, 918, -37, 61, -180,- -180, -180, -180, -180, -180, -180, -180, -180, -180, -180,- -180, -180, -180, -180, -180, -180, -180, -180, 528, 113,- 64, 69, 68, 87, 736, 736, 736, 736, 736, 736,- 736, 736, 736, 736, 736, 736, 736, 736, 736, 736,- 736, 736, 736, 736, 736, 736, 736, 736, -180, -180,- 1795, 79, -25, 8, 148, 124, -180, -180, -180, 1795,- 736, -180, -180, 1332, 1795, -20, -180, -180, 14, 736,- 555, -25, -25, 619, 92, -180, 18, -180, -180, -180,- -180, -180, -180, 395, -180, 773, 82, 1150, -180, 773,- 773, -180, 395, 1861, 184, 184, 1828, 334, 1881, 1861,- 1861, 1861, 1861, 1861, 1861, 184, 184, 1795, 1828, 1861,- 184, 184, -10, -10, 83, 83, 83, -180, 135, -25,- 837, 97, 90, 85, 736, 94, 869, 19, -180, -180,- 736, -180, 80, -180, 140, -2, -180, 1382, -180, 1582,- 99, 100, -180, -180, 736, -180, 736, -180, 114, -180,- 773, 117, 56, 98, 103, 117, 117, -180, -180, -180,- -12, 105, 736, 164, 107, -21, -180, 110, 736, -180,- -180, 967, -180, 709, 116, -180, 170, -180, -180, -180,- 14, 118, -180, 736, 736, -180, 736, 736, 1795, 1664,- -180, 773, 773, 773, -25, -180, -25, 1183, -180, -25,- 837, -180, -25, 1795, 119, 122, 1016, -180, -180, -180,- 736, 1713, 1762, 1432, 1482, -180, 117, 117, 117, -180,- -180, 120, -180, -180, -180, -180, -180, 127, 1532, -180,- 736, 736, 736, -25, -180, -180, 1582, 1216, 1065, -180,- -180, -180, 736, -180, 1249, -180-};-- /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.- Performed when YYTABLE does not specify something else to do. Zero- means the default is an error. */-static const yytype_uint8 yydefact[] =-{- 4, 0, 0, 6, 105, 81, 96, 98, 73, 0,- 0, 0, 0, 0, 0, 0, 60, 0, 0, 0,- 0, 0, 0, 0, 0, 97, 47, 1, 0, 0,- 8, 6, 0, 0, 77, 62, 0, 0, 0, 0,- 18, 0, 75, 0, 64, 32, 0, 0, 104, 103,- 84, 0, 0, 83, 0, 101, 0, 0, 154, 126,- 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,- 137, 138, 139, 140, 141, 142, 143, 144, 0, 0,- 152, 0, 0, 146, 0, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 0, 21, 5,- 10, 80, 0, 0, 0, 0, 53, 52, 3, 2,- 8, 7, 48, 0, 113, 0, 111, 64, 0, 0,- 0, 0, 0, 0, 0, 74, 0, 107, 99, 85,- 79, 108, 100, 0, 110, 0, 0, 0, 153, 0,- 0, 102, 0, 40, 41, 42, 25, 24, 23, 27,- 31, 34, 36, 39, 26, 45, 46, 28, 29, 22,- 43, 44, 30, 33, 35, 37, 38, 76, 0, 0,- 0, 0, 82, 0, 0, 89, 0, 0, 9, 49,- 0, 106, 0, 59, 0, 0, 56, 0, 16, 0,- 0, 0, 19, 17, 0, 65, 0, 61, 0, 148,- 0, 149, 71, 0, 0, 151, 150, 147, 114, 117,- 0, 0, 0, 0, 0, 0, 119, 0, 0, 78,- 109, 0, 88, 0, 87, 51, 0, 112, 63, 58,- 0, 0, 54, 0, 0, 15, 0, 0, 20, 0,- 70, 0, 0, 0, 0, 115, 0, 0, 121, 0,- 0, 116, 0, 11, 95, 94, 0, 86, 50, 57,- 0, 0, 0, 0, 0, 66, 69, 156, 155, 118,- 122, 0, 124, 120, 123, 92, 91, 93, 0, 68,- 0, 0, 0, 0, 90, 55, 0, 0, 0, 125,- 67, 12, 0, 14, 0, 13-};-- /* YYPGOTO[NTERM-NUM]. */-static const yytype_int16 yypgoto[] =-{- -180, -180, -180, 150, 66, -1, -180, -180, 158, -9,- -180, -50, 5, -180, -180, 65, -105, -144, -4, -180,- 7, -129, -180, -180, -67, -179, -115, -180-};-- /* YYDEFGOTO[NTERM-NUM]. */-static const yytype_int16 yydefgoto[] =-{- -1, 2, 3, 30, 118, 110, 31, 32, 115, 24,- 195, 196, 25, 44, 127, 136, 245, 211, 26, 125,- 126, 181, 220, 225, 226, 81, 82, 83-};-- /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If- positive, shift that token. If negative, reduce the rule whose- number is the opposite. If YYTABLE_NINF, syntax error. */-static const yytype_int16 yytable[] =-{- 23, 227, 200, 201, 42, 215, 216, 38, 39, 37,- 143, 111, 40, 27, 111, 52, 45, 47, 193, 112,- 56, 120, 131, 235, 48, 53, 260, 80, 209, 119,- 144, 123, 124, 116, 116, 254, 178, 217, 190, 179,- 191, 180, 1, 105, 106, 107, 261, 108, 16, 34,- 219, 49, 128, 255, 33, 129, 240, 111, 241, 205,- 206, 111, 207, 35, 43, 132, 250, 28, 29, 113,- 114, 36, 113, 114, 133, 194, 41, 147, 134, 135,- 236, 227, 137, 153, 154, 155, 156, 157, 158, 159,- 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,- 170, 171, 172, 173, 174, 175, 176, 276, 277, 278,- 139, 120, 140, 186, 141, 113, 114, 148, 182, 113,- 114, 205, 206, 145, 238, 279, 149, 280, 197, 199,- 282, 150, 203, 284, 152, 151, 177, 187, 204, 218,- 108, 212, 213, 228, 239, 212, 212, 229, 80, 183,- 230, 232, 4, 5, 6, 7, 8, 80, 246, 247,- 252, 143, 9, 251, 299, 253, 10, 256, 258, 259,- 11, 12, 262, 267, 268, 13, 285, 14, 15, 286,- 270, 121, 293, 231, 294, 224, 188, 117, 16, 124,- 269, 300, 192, 283, -146, -146, 0, 237, 0, 0,- 17, 0, 0, 248, 0, 249, 212, 18, 0, 19,- 184, 20, 21, 185, 22, 0, 0, 0, 0, 0,- 0, 257, -146, -146, 0, 0, 0, 263, 0, 0,- 0, 0, 266, -146, -146, 103, 104, 105, 106, 107,- 0, 108, 271, 272, 0, 273, 274, 212, 212, 212,- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 224, 0, 0, 0, 288,- 0, 0, 0, 0, -72, 50, 0, 0, 51, -72,- 0, 52, 0, -72, -72, -72, -72, -72, 0, 296,- 297, 298, 0, -72, -72, -72, 0, 0, -72, -72,- -72, 304, -72, 0, 0, 0, -72, -72, -72, -72,- -72, -72, -72, -72, 16, 0, 0, -72, 0, 0,- -72, -72, -72, -72, -72, -72, -72, -72, -72, -72,- 0, -72, -72, 0, -72, 0, -72, -72, -72, -72,- 57, -72, 0, 58, 85, 86, 52, 0, 0, 0,- 0, 0, 59, 60, 61, 62, 63, 64, 65, 66,- 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,- 77, 0, 96, 97, 0, 0, 0, 0, 0, 16,- 0, 0, 0, 101, 102, 103, 104, 105, 106, 107,- 0, 108, 0, 0, 0, 0, 208, 0, 78, 58,- 79, 0, 52, 0, 0, 0, -145, 0, 59, 60,- 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,- 71, 72, 73, 74, 75, 76, 77, 0, 0, 0,- 0, 0, 0, 0, 0, 16, 0, 0, 54, 0,- 0, 4, 5, 6, 7, 8, 0, 0, 0, 0,- 0, 9, 0, 0, 78, 10, 79, 0, 0, 11,- 12, 0, -145, 0, 13, 46, 14, 15, 4, 5,- 6, 7, 8, 0, 0, 0, 0, 16, 9, 0,- 0, 0, 10, 0, 0, 0, 11, 12, 0, 17,- 0, 13, 0, 14, 15, 0, 18, 0, 19, 0,- 20, 21, 55, 22, 16, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 17, 0, 0, 0,- 0, 0, 0, 18, 0, 19, 0, 20, 21, 146,- 22, 0, 4, 5, 6, 7, 8, 0, 0, 0,- 0, 0, 9, 0, 0, 0, 10, 0, 0, 0,- 11, 12, 0, 0, 0, 13, 198, 14, 15, 4,- 5, 6, 7, 8, 0, 0, 0, 0, 16, 9,- 0, 0, 0, 10, 0, 0, 0, 11, 12, 0,- 17, 0, 13, 0, 14, 15, 0, 18, 0, 19,- 0, 20, 21, 0, 22, 16, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 17, 0, 0,- 0, 0, 0, 0, 18, 0, 19, 0, 20, 21,- 202, 22, 0, 4, 5, 6, 7, 8, 0, 0,- 0, 0, 0, 9, 0, 0, 0, 10, 0, 0,- 0, 11, 12, 0, 0, 0, 13, 0, 14, 15,- 4, 5, 6, 7, 8, 0, 0, 0, 0, 16,- 9, 0, 0, 0, 10, 0, 0, 0, 11, 12,- 0, 17, 0, 13, 0, 14, 15, 0, 18, 0,- 19, 0, 20, 21, 0, 22, 16, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 0, 17, 0,- 0, 0, 0, 0, 122, 18, 0, 19, 0, 20,- 21, 0, 22, 4, 5, 6, 7, 8, 0, 0,- 0, 0, 0, 9, 0, 0, 0, 10, 0, 0,- 0, 11, 12, 0, 0, 0, 13, 0, 14, 15,- 4, 5, 6, 7, 8, 0, 0, 0, 0, 16,- 9, 0, 0, 0, 10, 0, 0, 0, 11, 12,- 0, 17, 0, 13, 0, 14, 15, 0, 18, 0,- 19, 0, 20, 21, 265, 22, 16, 4, 5, 6,- 7, 8, 0, 4, 5, 6, 7, 8, 17, 0,- 0, 0, 0, 0, 0, 18, 0, 19, 0, 20,- 21, 0, 22, 15, 0, 0, 0, 0, 0, 15,- 0, 0, 0, 16, 0, 0, 0, 0, 0, 16,- 0, 0, 0, 0, 0, 210, 0, 0, 0, 0,- 0, 0, 18, 0, 19, 0, 20, 21, 18, 22,- 19, 221, 20, 21, 52, 22, 0, 0, 0, 0,- 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,- 69, 70, 71, 72, 73, 74, 75, 76, 77, 0,- 0, 0, 0, 0, 0, 0, 0, 16, 84, 85,- 86, 87, 0, 0, 0, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 88, 89, 222, 0, 223, 0,- 0, 90, 91, 92, 93, 94, 95, 96, 97, 0,- 0, 0, 0, 0, 0, 98, 99, 100, 101, 102,- 103, 104, 105, 106, 107, 0, 108, 84, 85, 86,- 87, 233, 0, 0, 234, 0, 0, 0, 0, 0,- 0, 0, 0, 88, 89, 0, 0, 0, 0, 0,- 90, 91, 92, 93, 94, 95, 96, 97, 0, 0,- 0, 0, 0, 0, 98, 99, 100, 101, 102, 103,- 104, 105, 106, 107, 0, 108, 84, 85, 86, 87,- 0, 0, 0, 142, 0, 0, 0, 0, 0, 0,- 0, 0, 88, 89, 0, 0, 0, 0, 0, 90,- 91, 92, 93, 94, 95, 96, 97, 0, 0, 0,- 0, 0, 0, 98, 99, 100, 101, 102, 103, 104,- 105, 106, 107, 0, 108, 84, 85, 86, 87, 0,- 0, 0, 264, 0, 0, 0, 0, 0, 0, 0,- 0, 88, 89, 0, 0, 0, 0, 0, 90, 91,- 92, 93, 94, 95, 96, 97, 0, 0, 0, 0,- 0, 0, 98, 99, 100, 101, 102, 103, 104, 105,- 106, 107, 0, 108, 84, 85, 86, 87, 0, 0,- 0, 287, 0, 0, 0, 0, 0, 0, 0, 0,- 88, 89, 0, 0, 0, 0, 0, 90, 91, 92,- 93, 94, 95, 96, 97, 0, 0, 0, 0, 0,- 0, 98, 99, 100, 101, 102, 103, 104, 105, 106,- 107, 0, 108, 302, 0, 303, 84, 85, 86, 87,- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 0, 0, 88, 89, 0, 0, 0, 0, 0, 90,- 91, 92, 93, 94, 95, 96, 97, 0, 0, 84,- 85, 86, 87, 98, 99, 100, 101, 102, 103, 104,- 105, 106, 107, 0, 108, 88, 89, 138, 0, 0,- 0, 0, 90, 91, 92, 93, 94, 95, 96, 97,- 0, 0, 84, 85, 86, 87, 98, 99, 100, 101,- 102, 103, 104, 105, 106, 107, 0, 108, 88, 89,- 214, 0, 0, 0, 0, 90, 91, 92, 93, 94,- 95, 96, 97, 0, 0, 84, 85, 86, 87, 98,- 99, 100, 101, 102, 103, 104, 105, 106, 107, 0,- 108, 88, 89, 281, 0, 0, 0, 0, 90, 91,- 92, 93, 94, 95, 96, 97, 0, 0, 84, 85,- 86, 87, 98, 99, 100, 101, 102, 103, 104, 105,- 106, 107, 0, 108, 88, 89, 301, 0, 0, 0,- 0, 90, 91, 92, 93, 94, 95, 96, 97, 0,- 0, 84, 85, 86, 87, 98, 99, 100, 101, 102,- 103, 104, 105, 106, 107, 0, 108, 88, 89, 305,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 109, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 189, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 242, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 291, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 292, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 295, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 0, 243, 244, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 0, 0, 0, 0, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 84, 85, 86, 87, 0, 0, 0, 0, 0, 0,- 130, 0, 0, 0, 0, 0, 88, 89, 0, 0,- 0, 0, 0, 90, 91, 92, 93, 94, 95, 96,- 97, 0, 0, 84, 85, 86, 87, 98, 99, 100,- 101, 102, 103, 104, 105, 106, 107, 0, 108, 88,- 89, 0, 0, 0, 0, 0, 90, 91, 92, 93,- 94, 95, 96, 97, 0, 0, 0, 275, 0, 0,- 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,- 0, 108, 84, 85, 86, 87, 0, 0, 0, 0,- 0, 0, 0, 0, 0, 0, 0, 289, 88, 89,- 0, 0, 0, 0, 0, 90, 91, 92, 93, 94,- 95, 96, 97, 0, 0, 0, 0, 0, 0, 98,- 99, 100, 101, 102, 103, 104, 105, 106, 107, 0,- 108, 84, 85, 86, 87, 0, 0, 0, 0, 0,- 0, 290, 0, 0, 0, 0, 0, 88, 89, 0,- 0, 0, 0, 0, 90, 91, 92, 93, 94, 95,- 96, 97, 0, 0, 84, 85, 86, 87, 98, 99,- 100, 101, 102, 103, 104, 105, 106, 107, 0, 108,- 88, 89, 0, 0, 0, 0, 0, 90, 91, 92,- 93, 94, 95, 96, 97, 0, 0, 84, 85, 86,- 87, 98, 99, 100, 101, 102, 103, 104, 105, 106,- 107, 0, 108, 88, 89, 0, 0, 0, 0, 0,- 90, 91, 92, 93, 94, 95, 96, 97, 0, 0,- -146, 85, 86, 0, 0, 0, 100, 101, 102, 103,- 104, 105, 106, 107, 0, 108, 88, 89, 0, 0,- 0, 85, 86, -146, -146, -146, -146, -146, -146, 96,- 97, 0, 0, 0, 0, 0, 88, 0, 0, -146,- 101, 102, 103, 104, 105, 106, 107, 0, 108, 96,- 97, 0, 0, 0, 0, 0, 0, 0, 0, 0,- 101, 102, 103, 104, 105, 106, 107, 0, 108-};--static const yytype_int16 yycheck[] =-{- 1, 180, 131, 132, 1, 149, 150, 11, 12, 10,- 47, 5, 13, 0, 5, 7, 17, 18, 4, 13,- 21, 30, 13, 4, 4, 20, 47, 22, 143, 30,- 67, 32, 33, 28, 29, 47, 61, 152, 58, 64,- 60, 66, 15, 53, 54, 55, 67, 57, 40, 57,- 179, 31, 59, 65, 59, 62, 58, 5, 60, 41,- 42, 5, 44, 40, 61, 13, 210, 16, 17, 63,- 64, 4, 63, 64, 28, 61, 61, 78, 4, 4,- 61, 260, 60, 84, 85, 86, 87, 88, 89, 90,- 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,- 101, 102, 103, 104, 105, 106, 107, 251, 252, 253,- 1, 120, 57, 114, 65, 63, 64, 4, 113, 63,- 64, 41, 42, 62, 44, 254, 62, 256, 129, 130,- 259, 62, 133, 262, 47, 67, 57, 13, 46, 4,- 57, 145, 60, 46, 4, 149, 150, 57, 143, 1,- 65, 57, 4, 5, 6, 7, 8, 152, 59, 59,- 62, 47, 14, 46, 293, 62, 18, 62, 4, 62,- 22, 23, 62, 57, 4, 27, 57, 29, 30, 57,- 62, 31, 62, 184, 57, 180, 120, 29, 40, 190,- 240, 296, 127, 260, 10, 11, -1, 190, -1, -1,- 52, -1, -1, 204, -1, 206, 210, 59, -1, 61,- 62, 63, 64, 65, 66, -1, -1, -1, -1, -1,- -1, 222, 38, 39, -1, -1, -1, 228, -1, -1,- -1, -1, 233, 49, 50, 51, 52, 53, 54, 55,- -1, 57, 243, 244, -1, 246, 247, 251, 252, 253,- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, 260, -1, -1, -1, 270,- -1, -1, -1, -1, 0, 1, -1, -1, 4, 5,- -1, 7, -1, 9, 10, 11, 12, 13, -1, 290,- 291, 292, -1, 19, 20, 21, -1, -1, 24, 25,- 26, 302, 28, -1, -1, -1, 32, 33, 34, 35,- 36, 37, 38, 39, 40, -1, -1, 43, -1, -1,- 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,- -1, 57, 58, -1, 60, -1, 62, 63, 64, 65,- 1, 67, -1, 4, 10, 11, 7, -1, -1, -1,- -1, -1, 13, 14, 15, 16, 17, 18, 19, 20,- 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,- 31, -1, 38, 39, -1, -1, -1, -1, -1, 40,- -1, -1, -1, 49, 50, 51, 52, 53, 54, 55,- -1, 57, -1, -1, -1, -1, 1, -1, 59, 4,- 61, -1, 7, -1, -1, -1, 67, -1, 13, 14,- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,- 25, 26, 27, 28, 29, 30, 31, -1, -1, -1,- -1, -1, -1, -1, -1, 40, -1, -1, 1, -1,- -1, 4, 5, 6, 7, 8, -1, -1, -1, -1,- -1, 14, -1, -1, 59, 18, 61, -1, -1, 22,- 23, -1, 67, -1, 27, 1, 29, 30, 4, 5,- 6, 7, 8, -1, -1, -1, -1, 40, 14, -1,- -1, -1, 18, -1, -1, -1, 22, 23, -1, 52,- -1, 27, -1, 29, 30, -1, 59, -1, 61, -1,- 63, 64, 65, 66, 40, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, 52, -1, -1, -1,- -1, -1, -1, 59, -1, 61, -1, 63, 64, 1,- 66, -1, 4, 5, 6, 7, 8, -1, -1, -1,- -1, -1, 14, -1, -1, -1, 18, -1, -1, -1,- 22, 23, -1, -1, -1, 27, 1, 29, 30, 4,- 5, 6, 7, 8, -1, -1, -1, -1, 40, 14,- -1, -1, -1, 18, -1, -1, -1, 22, 23, -1,- 52, -1, 27, -1, 29, 30, -1, 59, -1, 61,- -1, 63, 64, -1, 66, 40, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 52, -1, -1,- -1, -1, -1, -1, 59, -1, 61, -1, 63, 64,- 1, 66, -1, 4, 5, 6, 7, 8, -1, -1,- -1, -1, -1, 14, -1, -1, -1, 18, -1, -1,- -1, 22, 23, -1, -1, -1, 27, -1, 29, 30,- 4, 5, 6, 7, 8, -1, -1, -1, -1, 40,- 14, -1, -1, -1, 18, -1, -1, -1, 22, 23,- -1, 52, -1, 27, -1, 29, 30, -1, 59, -1,- 61, -1, 63, 64, -1, 66, 40, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, -1, 52, -1,- -1, -1, -1, -1, 58, 59, -1, 61, -1, 63,- 64, -1, 66, 4, 5, 6, 7, 8, -1, -1,- -1, -1, -1, 14, -1, -1, -1, 18, -1, -1,- -1, 22, 23, -1, -1, -1, 27, -1, 29, 30,- 4, 5, 6, 7, 8, -1, -1, -1, -1, 40,- 14, -1, -1, -1, 18, -1, -1, -1, 22, 23,- -1, 52, -1, 27, -1, 29, 30, -1, 59, -1,- 61, -1, 63, 64, 65, 66, 40, 4, 5, 6,- 7, 8, -1, 4, 5, 6, 7, 8, 52, -1,- -1, -1, -1, -1, -1, 59, -1, 61, -1, 63,- 64, -1, 66, 30, -1, -1, -1, -1, -1, 30,- -1, -1, -1, 40, -1, -1, -1, -1, -1, 40,- -1, -1, -1, -1, -1, 52, -1, -1, -1, -1,- -1, -1, 59, -1, 61, -1, 63, 64, 59, 66,- 61, 4, 63, 64, 7, 66, -1, -1, -1, -1,- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,- 23, 24, 25, 26, 27, 28, 29, 30, 31, -1,- -1, -1, -1, -1, -1, -1, -1, 40, 9, 10,- 11, 12, -1, -1, -1, -1, -1, -1, -1, -1,- -1, -1, -1, -1, 25, 26, 59, -1, 61, -1,- -1, 32, 33, 34, 35, 36, 37, 38, 39, -1,- -1, -1, -1, -1, -1, 46, 47, 48, 49, 50,- 51, 52, 53, 54, 55, -1, 57, 9, 10, 11,- 12, 62, -1, -1, 65, -1, -1, -1, -1, -1,- -1, -1, -1, 25, 26, -1, -1, -1, -1, -1,- 32, 33, 34, 35, 36, 37, 38, 39, -1, -1,- -1, -1, -1, -1, 46, 47, 48, 49, 50, 51,- 52, 53, 54, 55, -1, 57, 9, 10, 11, 12,- -1, -1, -1, 65, -1, -1, -1, -1, -1, -1,- -1, -1, 25, 26, -1, -1, -1, -1, -1, 32,- 33, 34, 35, 36, 37, 38, 39, -1, -1, -1,- -1, -1, -1, 46, 47, 48, 49, 50, 51, 52,- 53, 54, 55, -1, 57, 9, 10, 11, 12, -1,- -1, -1, 65, -1, -1, -1, -1, -1, -1, -1,- -1, 25, 26, -1, -1, -1, -1, -1, 32, 33,- 34, 35, 36, 37, 38, 39, -1, -1, -1, -1,- -1, -1, 46, 47, 48, 49, 50, 51, 52, 53,- 54, 55, -1, 57, 9, 10, 11, 12, -1, -1,- -1, 65, -1, -1, -1, -1, -1, -1, -1, -1,- 25, 26, -1, -1, -1, -1, -1, 32, 33, 34,- 35, 36, 37, 38, 39, -1, -1, -1, -1, -1,- -1, 46, 47, 48, 49, 50, 51, 52, 53, 54,- 55, -1, 57, 58, -1, 60, 9, 10, 11, 12,- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,- -1, -1, 25, 26, -1, -1, -1, -1, -1, 32,- 33, 34, 35, 36, 37, 38, 39, -1, -1, 9,- 10, 11, 12, 46, 47, 48, 49, 50, 51, 52,- 53, 54, 55, -1, 57, 25, 26, 60, -1, -1,- -1, -1, 32, 33, 34, 35, 36, 37, 38, 39,- -1, -1, 9, 10, 11, 12, 46, 47, 48, 49,- 50, 51, 52, 53, 54, 55, -1, 57, 25, 26,- 60, -1, -1, -1, -1, 32, 33, 34, 35, 36,- 37, 38, 39, -1, -1, 9, 10, 11, 12, 46,- 47, 48, 49, 50, 51, 52, 53, 54, 55, -1,- 57, 25, 26, 60, -1, -1, -1, -1, 32, 33,- 34, 35, 36, 37, 38, 39, -1, -1, 9, 10,- 11, 12, 46, 47, 48, 49, 50, 51, 52, 53,- 54, 55, -1, 57, 25, 26, 60, -1, -1, -1,- -1, 32, 33, 34, 35, 36, 37, 38, 39, -1,- -1, 9, 10, 11, 12, 46, 47, 48, 49, 50,- 51, 52, 53, 54, 55, -1, 57, 25, 26, 60,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 58, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, -1, 20, 21, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, -1, -1, -1, -1, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 9, 10, 11, 12, -1, -1, -1, -1, -1, -1,- 19, -1, -1, -1, -1, -1, 25, 26, -1, -1,- -1, -1, -1, 32, 33, 34, 35, 36, 37, 38,- 39, -1, -1, 9, 10, 11, 12, 46, 47, 48,- 49, 50, 51, 52, 53, 54, 55, -1, 57, 25,- 26, -1, -1, -1, -1, -1, 32, 33, 34, 35,- 36, 37, 38, 39, -1, -1, -1, 43, -1, -1,- 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,- -1, 57, 9, 10, 11, 12, -1, -1, -1, -1,- -1, -1, -1, -1, -1, -1, -1, 24, 25, 26,- -1, -1, -1, -1, -1, 32, 33, 34, 35, 36,- 37, 38, 39, -1, -1, -1, -1, -1, -1, 46,- 47, 48, 49, 50, 51, 52, 53, 54, 55, -1,- 57, 9, 10, 11, 12, -1, -1, -1, -1, -1,- -1, 19, -1, -1, -1, -1, -1, 25, 26, -1,- -1, -1, -1, -1, 32, 33, 34, 35, 36, 37,- 38, 39, -1, -1, 9, 10, 11, 12, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, -1, 57,- 25, 26, -1, -1, -1, -1, -1, 32, 33, 34,- 35, 36, 37, 38, 39, -1, -1, 9, 10, 11,- 12, 46, 47, 48, 49, 50, 51, 52, 53, 54,- 55, -1, 57, 25, 26, -1, -1, -1, -1, -1,- 32, 33, 34, 35, 36, 37, 38, 39, -1, -1,- 9, 10, 11, -1, -1, -1, 48, 49, 50, 51,- 52, 53, 54, 55, -1, 57, 25, 26, -1, -1,- -1, 10, 11, 32, 33, 34, 35, 36, 37, 38,- 39, -1, -1, -1, -1, -1, 25, -1, -1, 48,- 49, 50, 51, 52, 53, 54, 55, -1, 57, 38,- 39, -1, -1, -1, -1, -1, -1, -1, -1, -1,- 49, 50, 51, 52, 53, 54, 55, -1, 57-};-- /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing- symbol of state STATE-NUM. */-static const yytype_uint8 yystos[] =-{- 0, 15, 69, 70, 4, 5, 6, 7, 8, 14,- 18, 22, 23, 27, 29, 30, 40, 52, 59, 61,- 63, 64, 66, 73, 77, 80, 86, 0, 16, 17,- 71, 74, 75, 59, 57, 40, 4, 73, 86, 86,- 73, 61, 1, 61, 81, 73, 1, 73, 4, 31,- 1, 4, 7, 80, 1, 65, 73, 1, 4, 13,- 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,- 24, 25, 26, 27, 28, 29, 30, 31, 59, 61,- 80, 93, 94, 95, 9, 10, 11, 12, 25, 26,- 32, 33, 34, 35, 36, 37, 38, 39, 46, 47,- 48, 49, 50, 51, 52, 53, 54, 55, 57, 58,- 73, 5, 13, 63, 64, 76, 80, 76, 72, 73,- 77, 71, 58, 73, 73, 87, 88, 82, 59, 62,- 19, 13, 13, 28, 4, 4, 83, 60, 60, 1,- 57, 65, 65, 47, 67, 62, 1, 73, 4, 62,- 62, 67, 47, 73, 73, 73, 73, 73, 73, 73,- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,- 73, 73, 73, 73, 73, 73, 73, 57, 61, 64,- 66, 89, 80, 1, 62, 65, 73, 13, 72, 58,- 58, 60, 83, 4, 61, 78, 79, 73, 1, 73,- 89, 89, 1, 73, 46, 41, 42, 44, 1, 94,- 52, 85, 86, 60, 60, 85, 85, 94, 4, 89,- 90, 4, 59, 61, 80, 91, 92, 93, 46, 57,- 65, 73, 57, 62, 65, 4, 61, 88, 44, 4,- 58, 60, 58, 20, 21, 84, 59, 59, 73, 73,- 85, 46, 62, 62, 47, 65, 62, 73, 4, 62,- 47, 67, 62, 73, 65, 65, 73, 57, 4, 79,- 62, 73, 73, 73, 73, 43, 85, 85, 85, 89,- 89, 60, 89, 92, 89, 57, 57, 65, 73, 24,- 19, 58, 58, 62, 57, 58, 73, 73, 73, 89,- 84, 60, 58, 60, 73, 60-};-- /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */-static const yytype_uint8 yyr1[] =-{- 0, 68, 69, 69, 70, 70, 71, 71, 72, 72,- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,- 73, 73, 73, 73, 73, 73, 73, 73, 73, 73,- 73, 73, 73, 73, 73, 73, 73, 73, 74, 74,- 75, 75, 75, 76, 77, 77, 78, 78, 79, 79,- 81, 80, 82, 80, 83, 83, 83, 84, 84, 85,- 85, 85, 86, 86, 86, 86, 86, 86, 86, 86,- 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,- 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,- 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,- 86, 87, 87, 88, 89, 89, 89, 90, 90, 91,- 91, 92, 92, 92, 92, 92, 93, 93, 93, 93,- 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,- 93, 93, 93, 93, 93, 94, 94, 94, 94, 95,- 95, 95, 95, 95, 95, 95, 95-};-- /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */-static const yytype_uint8 yyr2[] =-{- 0, 2, 3, 3, 0, 3, 0, 2, 0, 2,- 2, 5, 9, 11, 9, 5, 4, 4, 2, 4,- 5, 2, 3, 3, 3, 3, 3, 3, 3, 3,- 3, 3, 2, 3, 3, 3, 3, 3, 3, 3,- 3, 3, 3, 3, 3, 3, 3, 1, 2, 3,- 5, 4, 2, 1, 5, 8, 1, 3, 2, 1,- 0, 4, 0, 5, 0, 2, 4, 5, 3, 3,- 2, 1, 1, 1, 3, 2, 3, 2, 4, 3,- 2, 1, 3, 2, 2, 3, 5, 4, 4, 3,- 7, 6, 6, 6, 5, 5, 1, 1, 1, 3,- 3, 2, 3, 2, 2, 1, 4, 3, 3, 4,- 3, 1, 3, 1, 2, 3, 3, 1, 3, 1,- 3, 2, 3, 3, 3, 5, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,- 1, 1, 1, 1, 1, 0, 1, 3, 3, 3,- 3, 3, 1, 2, 1, 5, 5-};---#define yyerrok (yyerrstatus = 0)-#define yyclearin (yychar = YYEMPTY)-#define YYEMPTY (-2)-#define YYEOF 0--#define YYACCEPT goto yyacceptlab-#define YYABORT goto yyabortlab-#define YYERROR goto yyerrorlab---#define YYRECOVERING() (!!yyerrstatus)--#define YYBACKUP(Token, Value) \-do \- if (yychar == YYEMPTY) \- { \- yychar = (Token); \- yylval = (Value); \- YYPOPSTACK (yylen); \- yystate = *yyssp; \- goto yybackup; \- } \- else \- { \- yyerror (&yylloc, answer, errors, locations, lexer_param_ptr, YY_("syntax error: cannot back up")); \- YYERROR; \- } \-while (0)--/* Error token number */-#define YYTERROR 1-#define YYERRCODE 256---/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].- If N is 0, then set CURRENT to the empty location which ends- the previous symbol: RHS[0] (always defined). */--#ifndef YYLLOC_DEFAULT-# define YYLLOC_DEFAULT(Current, Rhs, N) \- do \- if (N) \- { \- (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \- (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \- (Current).last_line = YYRHSLOC (Rhs, N).last_line; \- (Current).last_column = YYRHSLOC (Rhs, N).last_column; \- } \- else \- { \- (Current).first_line = (Current).last_line = \- YYRHSLOC (Rhs, 0).last_line; \- (Current).first_column = (Current).last_column = \- YYRHSLOC (Rhs, 0).last_column; \- } \- while (0)-#endif--#define YYRHSLOC(Rhs, K) ((Rhs)[K])---/* Enable debugging if requested. */-#if YYDEBUG--# ifndef YYFPRINTF-# include <stdio.h> /* INFRINGES ON USER NAME SPACE */-# define YYFPRINTF fprintf-# endif--# define YYDPRINTF(Args) \-do { \- if (yydebug) \- YYFPRINTF Args; \-} while (0)---/* YY_LOCATION_PRINT -- Print the location on the stream.- This macro was not mandated originally: define only if we know- we won't break user code: when these are the locations we know. */--#ifndef YY_LOCATION_PRINT-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL--/* Print *YYLOCP on YYO. Private, do not rely on its existence. */--YY_ATTRIBUTE_UNUSED-static unsigned-yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp)-{- unsigned res = 0;- int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0;- if (0 <= yylocp->first_line)- {- res += YYFPRINTF (yyo, "%d", yylocp->first_line);- if (0 <= yylocp->first_column)- res += YYFPRINTF (yyo, ".%d", yylocp->first_column);- }- if (0 <= yylocp->last_line)- {- if (yylocp->first_line < yylocp->last_line)- {- res += YYFPRINTF (yyo, "-%d", yylocp->last_line);- if (0 <= end_col)- res += YYFPRINTF (yyo, ".%d", end_col);- }- else if (0 <= end_col && yylocp->first_column < end_col)- res += YYFPRINTF (yyo, "-%d", end_col);- }- return res;- }--# define YY_LOCATION_PRINT(File, Loc) \- yy_location_print_ (File, &(Loc))--# else-# define YY_LOCATION_PRINT(File, Loc) ((void) 0)-# endif-#endif---# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \-do { \- if (yydebug) \- { \- YYFPRINTF (stderr, "%s ", Title); \- yy_symbol_print (stderr, \- Type, Value, Location, answer, errors, locations, lexer_param_ptr); \- YYFPRINTF (stderr, "\n"); \- } \-} while (0)---/*----------------------------------------.-| Print this symbol's value on YYOUTPUT. |-`----------------------------------------*/--static void-yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr)-{- FILE *yyo = yyoutput;- YYUSE (yyo);- YYUSE (yylocationp);- YYUSE (answer);- YYUSE (errors);- YYUSE (locations);- YYUSE (lexer_param_ptr);- if (!yyvaluep)- return;-# ifdef YYPRINT- if (yytype < YYNTOKENS)- YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);-# endif- YYUSE (yytype);-}---/*--------------------------------.-| Print this symbol on YYOUTPUT. |-`--------------------------------*/--static void-yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr)-{- YYFPRINTF (yyoutput, "%s %s (",- yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);-- YY_LOCATION_PRINT (yyoutput, *yylocationp);- YYFPRINTF (yyoutput, ": ");- yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp, answer, errors, locations, lexer_param_ptr);- YYFPRINTF (yyoutput, ")");-}--/*------------------------------------------------------------------.-| yy_stack_print -- Print the state stack from its BOTTOM up to its |-| TOP (included). |-`------------------------------------------------------------------*/--static void-yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)-{- YYFPRINTF (stderr, "Stack now");- for (; yybottom <= yytop; yybottom++)- {- int yybot = *yybottom;- YYFPRINTF (stderr, " %d", yybot);- }- YYFPRINTF (stderr, "\n");-}--# define YY_STACK_PRINT(Bottom, Top) \-do { \- if (yydebug) \- yy_stack_print ((Bottom), (Top)); \-} while (0)---/*------------------------------------------------.-| Report that the YYRULE is going to be reduced. |-`------------------------------------------------*/--static void-yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule, block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr)-{- unsigned long int yylno = yyrline[yyrule];- int yynrhs = yyr2[yyrule];- int yyi;- YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",- yyrule - 1, yylno);- /* The symbols being reduced. */- for (yyi = 0; yyi < yynrhs; yyi++)- {- YYFPRINTF (stderr, " $%d = ", yyi + 1);- yy_symbol_print (stderr,- yystos[yyssp[yyi + 1 - yynrhs]],- &(yyvsp[(yyi + 1) - (yynrhs)])- , &(yylsp[(yyi + 1) - (yynrhs)]) , answer, errors, locations, lexer_param_ptr);- YYFPRINTF (stderr, "\n");- }-}--# define YY_REDUCE_PRINT(Rule) \-do { \- if (yydebug) \- yy_reduce_print (yyssp, yyvsp, yylsp, Rule, answer, errors, locations, lexer_param_ptr); \-} while (0)--/* Nonzero means print parse trace. It is left uninitialized so that- multiple parsers can coexist. */-int yydebug;-#else /* !YYDEBUG */-# define YYDPRINTF(Args)-# define YY_SYMBOL_PRINT(Title, Type, Value, Location)-# define YY_STACK_PRINT(Bottom, Top)-# define YY_REDUCE_PRINT(Rule)-#endif /* !YYDEBUG */---/* YYINITDEPTH -- initial size of the parser's stacks. */-#ifndef YYINITDEPTH-# define YYINITDEPTH 200-#endif--/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only- if the built-in stack extension method is used).-- Do not make this value too large; the results are undefined if- YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)- evaluated with infinite-precision integer arithmetic. */--#ifndef YYMAXDEPTH-# define YYMAXDEPTH 10000-#endif---#if YYERROR_VERBOSE--# ifndef yystrlen-# if defined __GLIBC__ && defined _STRING_H-# define yystrlen strlen-# else-/* Return the length of YYSTR. */-static YYSIZE_T-yystrlen (const char *yystr)-{- YYSIZE_T yylen;- for (yylen = 0; yystr[yylen]; yylen++)- continue;- return yylen;-}-# endif-# endif--# ifndef yystpcpy-# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE-# define yystpcpy stpcpy-# else-/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in- YYDEST. */-static char *-yystpcpy (char *yydest, const char *yysrc)-{- char *yyd = yydest;- const char *yys = yysrc;-- while ((*yyd++ = *yys++) != '\0')- continue;-- return yyd - 1;-}-# endif-# endif--# ifndef yytnamerr-/* Copy to YYRES the contents of YYSTR after stripping away unnecessary- quotes and backslashes, so that it's suitable for yyerror. The- heuristic is that double-quoting is unnecessary unless the string- contains an apostrophe, a comma, or backslash (other than- backslash-backslash). YYSTR is taken from yytname. If YYRES is- null, do not copy; instead, return the length of what the result- would have been. */-static YYSIZE_T-yytnamerr (char *yyres, const char *yystr)-{- if (*yystr == '"')- {- YYSIZE_T yyn = 0;- char const *yyp = yystr;-- for (;;)- switch (*++yyp)- {- case '\'':- case ',':- goto do_not_strip_quotes;-- case '\\':- if (*++yyp != '\\')- goto do_not_strip_quotes;- /* Fall through. */- default:- if (yyres)- yyres[yyn] = *yyp;- yyn++;- break;-- case '"':- if (yyres)- yyres[yyn] = '\0';- return yyn;- }- do_not_strip_quotes: ;- }-- if (! yyres)- return yystrlen (yystr);-- return yystpcpy (yyres, yystr) - yyres;-}-# endif--/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message- about the unexpected token YYTOKEN for the state stack whose top is- YYSSP.-- Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is- not large enough to hold the message. In that case, also set- *YYMSG_ALLOC to the required number of bytes. Return 2 if the- required number of bytes is too large to store. */-static int-yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,- yytype_int16 *yyssp, int yytoken)-{- YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);- YYSIZE_T yysize = yysize0;- enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };- /* Internationalized format string. */- const char *yyformat = YY_NULLPTR;- /* Arguments of yyformat. */- char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];- /* Number of reported tokens (one for the "unexpected", one per- "expected"). */- int yycount = 0;-- /* There are many possibilities here to consider:- - If this state is a consistent state with a default action, then- the only way this function was invoked is if the default action- is an error action. In that case, don't check for expected- tokens because there are none.- - The only way there can be no lookahead present (in yychar) is if- this state is a consistent state with a default action. Thus,- detecting the absence of a lookahead is sufficient to determine- that there is no unexpected or expected token to report. In that- case, just report a simple "syntax error".- - Don't assume there isn't a lookahead just because this state is a- consistent state with a default action. There might have been a- previous inconsistent state, consistent state with a non-default- action, or user semantic action that manipulated yychar.- - Of course, the expected token list depends on states to have- correct lookahead information, and it depends on the parser not- to perform extra reductions after fetching a lookahead from the- scanner and before detecting a syntax error. Thus, state merging- (from LALR or IELR) and default reductions corrupt the expected- token list. However, the list is correct for canonical LR with- one exception: it will still contain any token that will not be- accepted due to an error action in a later state.- */- if (yytoken != YYEMPTY)- {- int yyn = yypact[*yyssp];- yyarg[yycount++] = yytname[yytoken];- if (!yypact_value_is_default (yyn))- {- /* Start YYX at -YYN if negative to avoid negative indexes in- YYCHECK. In other words, skip the first -YYN actions for- this state because they are default actions. */- int yyxbegin = yyn < 0 ? -yyn : 0;- /* Stay within bounds of both yycheck and yytname. */- int yychecklim = YYLAST - yyn + 1;- int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;- int yyx;-- for (yyx = yyxbegin; yyx < yyxend; ++yyx)- if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR- && !yytable_value_is_error (yytable[yyx + yyn]))- {- if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)- {- yycount = 1;- yysize = yysize0;- break;- }- yyarg[yycount++] = yytname[yyx];- {- YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);- if (! (yysize <= yysize1- && yysize1 <= YYSTACK_ALLOC_MAXIMUM))- return 2;- yysize = yysize1;- }- }- }- }-- switch (yycount)- {-# define YYCASE_(N, S) \- case N: \- yyformat = S; \- break- YYCASE_(0, YY_("syntax error"));- YYCASE_(1, YY_("syntax error, unexpected %s"));- YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));- YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));- YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));- YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));-# undef YYCASE_- }-- {- YYSIZE_T yysize1 = yysize + yystrlen (yyformat);- if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))- return 2;- yysize = yysize1;- }-- if (*yymsg_alloc < yysize)- {- *yymsg_alloc = 2 * yysize;- if (! (yysize <= *yymsg_alloc- && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))- *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;- return 1;- }-- /* Avoid sprintf, as that infringes on the user's name space.- Don't have undefined behavior even if the translation- produced a string with the wrong number of "%s"s. */- {- char *yyp = *yymsg;- int yyi = 0;- while ((*yyp = *yyformat) != '\0')- if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)- {- yyp += yytnamerr (yyp, yyarg[yyi++]);- yyformat += 2;- }- else- {- yyp++;- yyformat++;- }- }- return 0;-}-#endif /* YYERROR_VERBOSE */--/*-----------------------------------------------.-| Release the memory associated to this symbol. |-`-----------------------------------------------*/--static void-yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr)-{- YYUSE (yyvaluep);- YYUSE (yylocationp);- YYUSE (answer);- YYUSE (errors);- YYUSE (locations);- YYUSE (lexer_param_ptr);- if (!yymsg)- yymsg = "Deleting";- YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);-- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN- switch (yytype)- {- case 4: /* IDENT */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 1866 "parser.c" /* yacc.c:1257 */- break;-- case 5: /* FIELD */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 1872 "parser.c" /* yacc.c:1257 */- break;-- case 6: /* LITERAL */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 1878 "parser.c" /* yacc.c:1257 */- break;-- case 7: /* FORMAT */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 1884 "parser.c" /* yacc.c:1257 */- break;-- case 41: /* QQSTRING_TEXT */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 1890 "parser.c" /* yacc.c:1257 */- break;-- case 70: /* Module */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1896 "parser.c" /* yacc.c:1257 */- break;-- case 71: /* Imports */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1902 "parser.c" /* yacc.c:1257 */- break;-- case 72: /* FuncDefs */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1908 "parser.c" /* yacc.c:1257 */- break;-- case 73: /* Exp */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1914 "parser.c" /* yacc.c:1257 */- break;-- case 74: /* Import */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1920 "parser.c" /* yacc.c:1257 */- break;-- case 75: /* ImportWhat */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1926 "parser.c" /* yacc.c:1257 */- break;-- case 76: /* ImportFrom */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1932 "parser.c" /* yacc.c:1257 */- break;-- case 77: /* FuncDef */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1938 "parser.c" /* yacc.c:1257 */- break;-- case 78: /* Params */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1944 "parser.c" /* yacc.c:1257 */- break;-- case 79: /* Param */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1950 "parser.c" /* yacc.c:1257 */- break;-- case 80: /* String */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1956 "parser.c" /* yacc.c:1257 */- break;-- case 83: /* QQString */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1962 "parser.c" /* yacc.c:1257 */- break;-- case 84: /* ElseBody */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1968 "parser.c" /* yacc.c:1257 */- break;-- case 85: /* ExpD */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1974 "parser.c" /* yacc.c:1257 */- break;-- case 86: /* Term */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1980 "parser.c" /* yacc.c:1257 */- break;-- case 87: /* Args */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1986 "parser.c" /* yacc.c:1257 */- break;-- case 88: /* Arg */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1992 "parser.c" /* yacc.c:1257 */- break;-- case 89: /* Pattern */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 1998 "parser.c" /* yacc.c:1257 */- break;-- case 90: /* ArrayPats */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 2004 "parser.c" /* yacc.c:1257 */- break;-- case 91: /* ObjPats */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 2010 "parser.c" /* yacc.c:1257 */- break;-- case 92: /* ObjPat */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 2016 "parser.c" /* yacc.c:1257 */- break;-- case 93: /* Keyword */-#line 36 "parser.y" /* yacc.c:1257 */- { jv_free(((*yyvaluep).literal)); }-#line 2022 "parser.c" /* yacc.c:1257 */- break;-- case 94: /* MkDict */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 2028 "parser.c" /* yacc.c:1257 */- break;-- case 95: /* MkDictPair */-#line 37 "parser.y" /* yacc.c:1257 */- { block_free(((*yyvaluep).blk)); }-#line 2034 "parser.c" /* yacc.c:1257 */- break;--- default:- break;- }- YY_IGNORE_MAYBE_UNINITIALIZED_END-}-----/*----------.-| yyparse. |-`----------*/--int-yyparse (block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr)-{-/* The lookahead symbol. */-int yychar;---/* The semantic value of the lookahead symbol. */-/* Default value used for initialization, for pacifying older GCCs- or non-GCC compilers. */-YY_INITIAL_VALUE (static YYSTYPE yyval_default;)-YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default);--/* Location data for the lookahead symbol. */-static YYLTYPE yyloc_default-# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL- = { 1, 1, 1, 1 }-# endif-;-YYLTYPE yylloc = yyloc_default;-- /* Number of syntax errors so far. */- int yynerrs;-- int yystate;- /* Number of tokens to shift before error messages enabled. */- int yyerrstatus;-- /* The stacks and their tools:- 'yyss': related to states.- 'yyvs': related to semantic values.- 'yyls': related to locations.-- Refer to the stacks through separate pointers, to allow yyoverflow- to reallocate them elsewhere. */-- /* The state stack. */- yytype_int16 yyssa[YYINITDEPTH];- yytype_int16 *yyss;- yytype_int16 *yyssp;-- /* The semantic value stack. */- YYSTYPE yyvsa[YYINITDEPTH];- YYSTYPE *yyvs;- YYSTYPE *yyvsp;-- /* The location stack. */- YYLTYPE yylsa[YYINITDEPTH];- YYLTYPE *yyls;- YYLTYPE *yylsp;-- /* The locations where the error started and ended. */- YYLTYPE yyerror_range[3];-- YYSIZE_T yystacksize;-- int yyn;- int yyresult;- /* Lookahead token as an internal (translated) token number. */- int yytoken = 0;- /* The variables used to return semantic value and location from the- action routines. */- YYSTYPE yyval;- YYLTYPE yyloc;--#if YYERROR_VERBOSE- /* Buffer for error messages, and its allocated size. */- char yymsgbuf[128];- char *yymsg = yymsgbuf;- YYSIZE_T yymsg_alloc = sizeof yymsgbuf;-#endif--#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))-- /* The number of symbols on the RHS of the reduced rule.- Keep to zero when no symbol should be popped. */- int yylen = 0;-- yyssp = yyss = yyssa;- yyvsp = yyvs = yyvsa;- yylsp = yyls = yylsa;- yystacksize = YYINITDEPTH;-- YYDPRINTF ((stderr, "Starting parse\n"));-- yystate = 0;- yyerrstatus = 0;- yynerrs = 0;- yychar = YYEMPTY; /* Cause a token to be read. */- yylsp[0] = yylloc;- goto yysetstate;--/*------------------------------------------------------------.-| yynewstate -- Push a new state, which is found in yystate. |-`------------------------------------------------------------*/- yynewstate:- /* In all cases, when you get here, the value and location stacks- have just been pushed. So pushing a state here evens the stacks. */- yyssp++;-- yysetstate:- *yyssp = yystate;-- if (yyss + yystacksize - 1 <= yyssp)- {- /* Get the current used size of the three stacks, in elements. */- YYSIZE_T yysize = yyssp - yyss + 1;--#ifdef yyoverflow- {- /* Give user a chance to reallocate the stack. Use copies of- these so that the &'s don't force the real ones into- memory. */- YYSTYPE *yyvs1 = yyvs;- yytype_int16 *yyss1 = yyss;- YYLTYPE *yyls1 = yyls;-- /* Each stack pointer address is followed by the size of the- data in use in that stack, in bytes. This used to be a- conditional around just the two extra args, but that might- be undefined if yyoverflow is a macro. */- yyoverflow (YY_("memory exhausted"),- &yyss1, yysize * sizeof (*yyssp),- &yyvs1, yysize * sizeof (*yyvsp),- &yyls1, yysize * sizeof (*yylsp),- &yystacksize);-- yyls = yyls1;- yyss = yyss1;- yyvs = yyvs1;- }-#else /* no yyoverflow */-# ifndef YYSTACK_RELOCATE- goto yyexhaustedlab;-# else- /* Extend the stack our own way. */- if (YYMAXDEPTH <= yystacksize)- goto yyexhaustedlab;- yystacksize *= 2;- if (YYMAXDEPTH < yystacksize)- yystacksize = YYMAXDEPTH;-- {- yytype_int16 *yyss1 = yyss;- union yyalloc *yyptr =- (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));- if (! yyptr)- goto yyexhaustedlab;- YYSTACK_RELOCATE (yyss_alloc, yyss);- YYSTACK_RELOCATE (yyvs_alloc, yyvs);- YYSTACK_RELOCATE (yyls_alloc, yyls);-# undef YYSTACK_RELOCATE- if (yyss1 != yyssa)- YYSTACK_FREE (yyss1);- }-# endif-#endif /* no yyoverflow */-- yyssp = yyss + yysize - 1;- yyvsp = yyvs + yysize - 1;- yylsp = yyls + yysize - 1;-- YYDPRINTF ((stderr, "Stack size increased to %lu\n",- (unsigned long int) yystacksize));-- if (yyss + yystacksize - 1 <= yyssp)- YYABORT;- }-- YYDPRINTF ((stderr, "Entering state %d\n", yystate));-- if (yystate == YYFINAL)- YYACCEPT;-- goto yybackup;--/*-----------.-| yybackup. |-`-----------*/-yybackup:-- /* Do appropriate processing given the current state. Read a- lookahead token if we need one and don't already have one. */-- /* First try to decide what to do without reference to lookahead token. */- yyn = yypact[yystate];- if (yypact_value_is_default (yyn))- goto yydefault;-- /* Not known => get a lookahead token if don't already have one. */-- /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */- if (yychar == YYEMPTY)- {- YYDPRINTF ((stderr, "Reading a token: "));- yychar = yylex (&yylval, &yylloc, answer, errors, locations, lexer_param_ptr);- }-- if (yychar <= YYEOF)- {- yychar = yytoken = YYEOF;- YYDPRINTF ((stderr, "Now at end of input.\n"));- }- else- {- yytoken = YYTRANSLATE (yychar);- YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);- }-- /* If the proper action on seeing token YYTOKEN is to reduce or to- detect an error, take that action. */- yyn += yytoken;- if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)- goto yydefault;- yyn = yytable[yyn];- if (yyn <= 0)- {- if (yytable_value_is_error (yyn))- goto yyerrlab;- yyn = -yyn;- goto yyreduce;- }-- /* Count tokens shifted since error; after three, turn off error- status. */- if (yyerrstatus)- yyerrstatus--;-- /* Shift the lookahead token. */- YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);-- /* Discard the shifted token. */- yychar = YYEMPTY;-- yystate = yyn;- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN- *++yyvsp = yylval;- YY_IGNORE_MAYBE_UNINITIALIZED_END- *++yylsp = yylloc;- goto yynewstate;---/*-----------------------------------------------------------.-| yydefault -- do the default action for the current state. |-`-----------------------------------------------------------*/-yydefault:- yyn = yydefact[yystate];- if (yyn == 0)- goto yyerrlab;- goto yyreduce;---/*-----------------------------.-| yyreduce -- Do a reduction. |-`-----------------------------*/-yyreduce:- /* yyn is the number of a rule to reduce with. */- yylen = yyr2[yyn];-- /* If YYLEN is nonzero, implement the default value of the action:- '$$ = $1'.-- Otherwise, the following line sets YYVAL to garbage.- This behavior is undocumented and Bison- users should not rely upon it. Assigning to YYVAL- unconditionally makes the parser a bit smaller, and it avoids a- GCC warning that YYVAL may be used uninitialized. */- yyval = yyvsp[1-yylen];-- /* Default location. */- YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);- YY_REDUCE_PRINT (yyn);- switch (yyn)- {- case 2:-#line 291 "parser.y" /* yacc.c:1646 */- {- *answer = BLOCK((yyvsp[-2].blk), (yyvsp[-1].blk), gen_op_simple(TOP), (yyvsp[0].blk));-}-#line 2330 "parser.c" /* yacc.c:1646 */- break;-- case 3:-#line 294 "parser.y" /* yacc.c:1646 */- {- *answer = BLOCK((yyvsp[-2].blk), (yyvsp[-1].blk), (yyvsp[0].blk));-}-#line 2338 "parser.c" /* yacc.c:1646 */- break;-- case 4:-#line 299 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_noop();-}-#line 2346 "parser.c" /* yacc.c:1646 */- break;-- case 5:-#line 302 "parser.y" /* yacc.c:1646 */- {- if (!block_is_const((yyvsp[-1].blk))) {- FAIL((yyloc), "Module metadata must be constant");- (yyval.blk) = gen_noop();- block_free((yyvsp[-1].blk));- } else {- (yyval.blk) = gen_module((yyvsp[-1].blk));- }-}-#line 2360 "parser.c" /* yacc.c:1646 */- break;-- case 6:-#line 313 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_noop();-}-#line 2368 "parser.c" /* yacc.c:1646 */- break;-- case 7:-#line 316 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-1].blk), (yyvsp[0].blk));-}-#line 2376 "parser.c" /* yacc.c:1646 */- break;-- case 8:-#line 321 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_noop();-}-#line 2384 "parser.c" /* yacc.c:1646 */- break;-- case 9:-#line 324 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_bind((yyvsp[-1].blk), (yyvsp[0].blk), OP_IS_CALL_PSEUDO);-}-#line 2392 "parser.c" /* yacc.c:1646 */- break;-- case 10:-#line 329 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_bind_referenced((yyvsp[-1].blk), (yyvsp[0].blk), OP_IS_CALL_PSEUDO);-}-#line 2400 "parser.c" /* yacc.c:1646 */- break;-- case 11:-#line 333 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_destructure((yyvsp[-4].blk), (yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2408 "parser.c" /* yacc.c:1646 */- break;-- case 12:-#line 337 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_reduce((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk));-}-#line 2416 "parser.c" /* yacc.c:1646 */- break;-- case 13:-#line 341 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_foreach((yyvsp[-9].blk), (yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk));-}-#line 2424 "parser.c" /* yacc.c:1646 */- break;-- case 14:-#line 345 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_foreach((yyvsp[-7].blk), (yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), gen_noop());-}-#line 2432 "parser.c" /* yacc.c:1646 */- break;-- case 15:-#line 349 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk));-}-#line 2440 "parser.c" /* yacc.c:1646 */- break;-- case 16:-#line 352 "parser.y" /* yacc.c:1646 */- {- FAIL((yyloc), "Possibly unterminated 'if' statement");- (yyval.blk) = (yyvsp[-2].blk);-}-#line 2449 "parser.c" /* yacc.c:1646 */- break;-- case 17:-#line 357 "parser.y" /* yacc.c:1646 */- {- //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, $4);- (yyval.blk) = gen_try((yyvsp[-2].blk), gen_try_handler((yyvsp[0].blk)));-}-#line 2458 "parser.c" /* yacc.c:1646 */- break;-- case 18:-#line 361 "parser.y" /* yacc.c:1646 */- {- //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, gen_op_simple(BACKTRACK));- (yyval.blk) = gen_try((yyvsp[0].blk), gen_op_simple(BACKTRACK));-}-#line 2467 "parser.c" /* yacc.c:1646 */- break;-- case 19:-#line 365 "parser.y" /* yacc.c:1646 */- {- FAIL((yyloc), "Possibly unterminated 'try' statement");- (yyval.blk) = (yyvsp[-2].blk);-}-#line 2476 "parser.c" /* yacc.c:1646 */- break;-- case 20:-#line 370 "parser.y" /* yacc.c:1646 */- {- jv v = jv_string_fmt("*label-%s", jv_string_value((yyvsp[-2].literal)));- (yyval.blk) = gen_location((yyloc), locations, gen_label(jv_string_value(v), (yyvsp[0].blk)));- jv_free((yyvsp[-2].literal));- jv_free(v);-}-#line 2487 "parser.c" /* yacc.c:1646 */- break;-- case 21:-#line 377 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_try((yyvsp[-1].blk), gen_op_simple(BACKTRACK));-}-#line 2495 "parser.c" /* yacc.c:1646 */- break;-- case 22:-#line 381 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_call("_assign", BLOCK(gen_lambda((yyvsp[-2].blk)), gen_lambda((yyvsp[0].blk))));-}-#line 2503 "parser.c" /* yacc.c:1646 */- break;-- case 23:-#line 385 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_or((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2511 "parser.c" /* yacc.c:1646 */- break;-- case 24:-#line 389 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_and((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2519 "parser.c" /* yacc.c:1646 */- break;-- case 25:-#line 393 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_definedor((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2527 "parser.c" /* yacc.c:1646 */- break;-- case 26:-#line 397 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_definedor_assign((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2535 "parser.c" /* yacc.c:1646 */- break;-- case 27:-#line 401 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_call("_modify", BLOCK(gen_lambda((yyvsp[-2].blk)), gen_lambda((yyvsp[0].blk))));-}-#line 2543 "parser.c" /* yacc.c:1646 */- break;-- case 28:-#line 405 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2551 "parser.c" /* yacc.c:1646 */- break;-- case 29:-#line 409 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_both((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2559 "parser.c" /* yacc.c:1646 */- break;-- case 30:-#line 413 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '+');-}-#line 2567 "parser.c" /* yacc.c:1646 */- break;-- case 31:-#line 417 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '+');-}-#line 2575 "parser.c" /* yacc.c:1646 */- break;-- case 32:-#line 421 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop()));-}-#line 2583 "parser.c" /* yacc.c:1646 */- break;-- case 33:-#line 425 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '-');-}-#line 2591 "parser.c" /* yacc.c:1646 */- break;-- case 34:-#line 429 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '-');-}-#line 2599 "parser.c" /* yacc.c:1646 */- break;-- case 35:-#line 433 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '*');-}-#line 2607 "parser.c" /* yacc.c:1646 */- break;-- case 36:-#line 437 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '*');-}-#line 2615 "parser.c" /* yacc.c:1646 */- break;-- case 37:-#line 441 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '/');- if (block_is_const_inf((yyval.blk)))- FAIL((yyloc), "Division by zero?");-}-#line 2625 "parser.c" /* yacc.c:1646 */- break;-- case 38:-#line 447 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '%');- if (block_is_const_inf((yyval.blk)))- FAIL((yyloc), "Remainder by zero?");-}-#line 2635 "parser.c" /* yacc.c:1646 */- break;-- case 39:-#line 453 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '/');-}-#line 2643 "parser.c" /* yacc.c:1646 */- break;-- case 40:-#line 457 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_update((yyvsp[-2].blk), (yyvsp[0].blk), '%');-}-#line 2651 "parser.c" /* yacc.c:1646 */- break;-- case 41:-#line 461 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), EQ);-}-#line 2659 "parser.c" /* yacc.c:1646 */- break;-- case 42:-#line 465 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), NEQ);-}-#line 2667 "parser.c" /* yacc.c:1646 */- break;-- case 43:-#line 469 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '<');-}-#line 2675 "parser.c" /* yacc.c:1646 */- break;-- case 44:-#line 473 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), '>');-}-#line 2683 "parser.c" /* yacc.c:1646 */- break;-- case 45:-#line 477 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), LESSEQ);-}-#line 2691 "parser.c" /* yacc.c:1646 */- break;-- case 46:-#line 481 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-2].blk), (yyvsp[0].blk), GREATEREQ);-}-#line 2699 "parser.c" /* yacc.c:1646 */- break;-- case 47:-#line 485 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 2707 "parser.c" /* yacc.c:1646 */- break;-- case 48:-#line 490 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[-1].blk);-}-#line 2715 "parser.c" /* yacc.c:1646 */- break;-- case 49:-#line 493 "parser.y" /* yacc.c:1646 */- {- if (!block_is_const((yyvsp[-1].blk))) {- FAIL((yyloc), "Module metadata must be constant");- (yyval.blk) = gen_noop();- block_free((yyvsp[-2].blk));- block_free((yyvsp[-1].blk));- } else if (block_const_kind((yyvsp[-1].blk)) != JV_KIND_OBJECT) {- FAIL((yyloc), "Module metadata must be an object");- (yyval.blk) = gen_noop();- block_free((yyvsp[-2].blk));- block_free((yyvsp[-1].blk));- } else {- (yyval.blk) = gen_import_meta((yyvsp[-2].blk), (yyvsp[-1].blk));- }-}-#line 2735 "parser.c" /* yacc.c:1646 */- break;-- case 50:-#line 510 "parser.y" /* yacc.c:1646 */- {- jv v = block_const((yyvsp[-3].blk));- // XXX Make gen_import take only blocks and the int is_data so we- // don't have to free so much stuff here- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 1);- block_free((yyvsp[-3].blk));- jv_free((yyvsp[0].literal));- jv_free(v);-}-#line 2749 "parser.c" /* yacc.c:1646 */- break;-- case 51:-#line 519 "parser.y" /* yacc.c:1646 */- {- jv v = block_const((yyvsp[-2].blk));- (yyval.blk) = gen_import(jv_string_value(v), jv_string_value((yyvsp[0].literal)), 0);- block_free((yyvsp[-2].blk));- jv_free((yyvsp[0].literal));- jv_free(v);-}-#line 2761 "parser.c" /* yacc.c:1646 */- break;-- case 52:-#line 526 "parser.y" /* yacc.c:1646 */- {- jv v = block_const((yyvsp[0].blk));- (yyval.blk) = gen_import(jv_string_value(v), NULL, 0);- block_free((yyvsp[0].blk));- jv_free(v);-}-#line 2772 "parser.c" /* yacc.c:1646 */- break;-- case 53:-#line 534 "parser.y" /* yacc.c:1646 */- {- if (!block_is_const((yyvsp[0].blk))) {- FAIL((yyloc), "Import path must be constant");- (yyval.blk) = gen_const(jv_string(""));- block_free((yyvsp[0].blk));- } else {- (yyval.blk) = (yyvsp[0].blk);- }-}-#line 2786 "parser.c" /* yacc.c:1646 */- break;-- case 54:-#line 545 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_function(jv_string_value((yyvsp[-3].literal)), gen_noop(), (yyvsp[-1].blk));- jv_free((yyvsp[-3].literal));-}-#line 2795 "parser.c" /* yacc.c:1646 */- break;-- case 55:-#line 550 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_function(jv_string_value((yyvsp[-6].literal)), (yyvsp[-4].blk), (yyvsp[-1].blk));- jv_free((yyvsp[-6].literal));-}-#line 2804 "parser.c" /* yacc.c:1646 */- break;-- case 56:-#line 556 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 2812 "parser.c" /* yacc.c:1646 */- break;-- case 57:-#line 559 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2820 "parser.c" /* yacc.c:1646 */- break;-- case 58:-#line 564 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_param_regular(jv_string_value((yyvsp[0].literal)));- jv_free((yyvsp[0].literal));-}-#line 2829 "parser.c" /* yacc.c:1646 */- break;-- case 59:-#line 569 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_param(jv_string_value((yyvsp[0].literal)));- jv_free((yyvsp[0].literal));-}-#line 2838 "parser.c" /* yacc.c:1646 */- break;-- case 60:-#line 576 "parser.y" /* yacc.c:1646 */- { (yyval.literal) = jv_string("text"); }-#line 2844 "parser.c" /* yacc.c:1646 */- break;-- case 61:-#line 576 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[-1].blk);- jv_free((yyvsp[-2].literal));-}-#line 2853 "parser.c" /* yacc.c:1646 */- break;-- case 62:-#line 580 "parser.y" /* yacc.c:1646 */- { (yyval.literal) = (yyvsp[-1].literal); }-#line 2859 "parser.c" /* yacc.c:1646 */- break;-- case 63:-#line 580 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[-1].blk);- jv_free((yyvsp[-2].literal));-}-#line 2868 "parser.c" /* yacc.c:1646 */- break;-- case 64:-#line 587 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_const(jv_string(""));-}-#line 2876 "parser.c" /* yacc.c:1646 */- break;-- case 65:-#line 590 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-1].blk), gen_const((yyvsp[0].literal)), '+');-}-#line 2884 "parser.c" /* yacc.c:1646 */- break;-- case 66:-#line 593 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_binop((yyvsp[-3].blk), gen_format((yyvsp[-1].blk), jv_copy((yyvsp[-4].literal))), '+');-}-#line 2892 "parser.c" /* yacc.c:1646 */- break;-- case 67:-#line 599 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_cond((yyvsp[-3].blk), (yyvsp[-1].blk), (yyvsp[0].blk));-}-#line 2900 "parser.c" /* yacc.c:1646 */- break;-- case 68:-#line 602 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[-1].blk);-}-#line 2908 "parser.c" /* yacc.c:1646 */- break;-- case 69:-#line 607 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_join((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 2916 "parser.c" /* yacc.c:1646 */- break;-- case 70:-#line 610 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[0].blk), gen_call("_negate", gen_noop()));-}-#line 2924 "parser.c" /* yacc.c:1646 */- break;-- case 71:-#line 613 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 2932 "parser.c" /* yacc.c:1646 */- break;-- case 72:-#line 619 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_noop();-}-#line 2940 "parser.c" /* yacc.c:1646 */- break;-- case 73:-#line 622 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_call("recurse", gen_noop());-}-#line 2948 "parser.c" /* yacc.c:1646 */- break;-- case 74:-#line 625 "parser.y" /* yacc.c:1646 */- {- jv v = jv_string_fmt("*label-%s", jv_string_value((yyvsp[0].literal))); // impossible symbol- (yyval.blk) = gen_location((yyloc), locations,- BLOCK(gen_op_unbound(LOADV, jv_string_value(v)),- gen_call("error", gen_noop())));- jv_free(v);- jv_free((yyvsp[0].literal));-}-#line 2961 "parser.c" /* yacc.c:1646 */- break;-- case 75:-#line 633 "parser.y" /* yacc.c:1646 */- {- FAIL((yyloc), "break requires a label to break to");- (yyval.blk) = gen_noop();-}-#line 2970 "parser.c" /* yacc.c:1646 */- break;-- case 76:-#line 637 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index_opt((yyvsp[-2].blk), gen_const((yyvsp[-1].literal)));-}-#line 2978 "parser.c" /* yacc.c:1646 */- break;-- case 77:-#line 640 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index_opt(gen_noop(), gen_const((yyvsp[-1].literal)));-}-#line 2986 "parser.c" /* yacc.c:1646 */- break;-- case 78:-#line 643 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index_opt((yyvsp[-3].blk), (yyvsp[-1].blk));-}-#line 2994 "parser.c" /* yacc.c:1646 */- break;-- case 79:-#line 646 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index_opt(gen_noop(), (yyvsp[-1].blk));-}-#line 3002 "parser.c" /* yacc.c:1646 */- break;-- case 80:-#line 649 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index((yyvsp[-1].blk), gen_const((yyvsp[0].literal)));-}-#line 3010 "parser.c" /* yacc.c:1646 */- break;-- case 81:-#line 652 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index(gen_noop(), gen_const((yyvsp[0].literal)));-}-#line 3018 "parser.c" /* yacc.c:1646 */- break;-- case 82:-#line 655 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 3026 "parser.c" /* yacc.c:1646 */- break;-- case 83:-#line 658 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index(gen_noop(), (yyvsp[0].blk));-}-#line 3034 "parser.c" /* yacc.c:1646 */- break;-- case 84:-#line 661 "parser.y" /* yacc.c:1646 */- {- FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields");- (yyval.blk) = gen_noop();-}-#line 3043 "parser.c" /* yacc.c:1646 */- break;-- case 85:-#line 665 "parser.y" /* yacc.c:1646 */- {- jv_free((yyvsp[-1].literal));- FAIL((yyloc), "try .[\"field\"] instead of .field for unusually named fields");- (yyval.blk) = gen_noop();-}-#line 3053 "parser.c" /* yacc.c:1646 */- break;-- case 86:-#line 671 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index_opt((yyvsp[-4].blk), (yyvsp[-2].blk));-}-#line 3061 "parser.c" /* yacc.c:1646 */- break;-- case 87:-#line 674 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_index((yyvsp[-3].blk), (yyvsp[-1].blk));-}-#line 3069 "parser.c" /* yacc.c:1646 */- break;-- case 88:-#line 677 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_join((yyvsp[-3].blk), gen_op_simple(EACH_OPT));-}-#line 3077 "parser.c" /* yacc.c:1646 */- break;-- case 89:-#line 680 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = block_join((yyvsp[-2].blk), gen_op_simple(EACH));-}-#line 3085 "parser.c" /* yacc.c:1646 */- break;-- case 90:-#line 683 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-6].blk), (yyvsp[-4].blk), (yyvsp[-2].blk), INDEX_OPT);-}-#line 3093 "parser.c" /* yacc.c:1646 */- break;-- case 91:-#line 686 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), gen_const(jv_null()), INDEX_OPT);-}-#line 3101 "parser.c" /* yacc.c:1646 */- break;-- case 92:-#line 689 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-5].blk), gen_const(jv_null()), (yyvsp[-2].blk), INDEX_OPT);-}-#line 3109 "parser.c" /* yacc.c:1646 */- break;-- case 93:-#line 692 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-5].blk), (yyvsp[-3].blk), (yyvsp[-1].blk), INDEX);-}-#line 3117 "parser.c" /* yacc.c:1646 */- break;-- case 94:-#line 695 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-4].blk), (yyvsp[-2].blk), gen_const(jv_null()), INDEX);-}-#line 3125 "parser.c" /* yacc.c:1646 */- break;-- case 95:-#line 698 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_slice_index((yyvsp[-4].blk), gen_const(jv_null()), (yyvsp[-1].blk), INDEX);-}-#line 3133 "parser.c" /* yacc.c:1646 */- break;-- case 96:-#line 701 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_const((yyvsp[0].literal));-}-#line 3141 "parser.c" /* yacc.c:1646 */- break;-- case 97:-#line 704 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 3149 "parser.c" /* yacc.c:1646 */- break;-- case 98:-#line 707 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_format(gen_noop(), (yyvsp[0].literal));-}-#line 3157 "parser.c" /* yacc.c:1646 */- break;-- case 99:-#line 710 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[-1].blk);-}-#line 3165 "parser.c" /* yacc.c:1646 */- break;-- case 100:-#line 713 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_collect((yyvsp[-1].blk));-}-#line 3173 "parser.c" /* yacc.c:1646 */- break;-- case 101:-#line 716 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_const(jv_array());-}-#line 3181 "parser.c" /* yacc.c:1646 */- break;-- case 102:-#line 719 "parser.y" /* yacc.c:1646 */- {- block o = gen_const_object((yyvsp[-1].blk));- if (o.first != NULL)- (yyval.blk) = o;- else- (yyval.blk) = BLOCK(gen_subexp(gen_const(jv_object())), (yyvsp[-1].blk), gen_op_simple(POP));-}-#line 3193 "parser.c" /* yacc.c:1646 */- break;-- case 103:-#line 726 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_const(JV_OBJECT(jv_string("file"), jv_copy(locations->fname),- jv_string("line"), jv_number(locfile_get_line(locations, (yyloc).start) + 1)));-}-#line 3202 "parser.c" /* yacc.c:1646 */- break;-- case 104:-#line 730 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal))));- jv_free((yyvsp[0].literal));-}-#line 3211 "parser.c" /* yacc.c:1646 */- break;-- case 105:-#line 734 "parser.y" /* yacc.c:1646 */- {- const char *s = jv_string_value((yyvsp[0].literal));- if (strcmp(s, "false") == 0)- (yyval.blk) = gen_const(jv_false());- else if (strcmp(s, "true") == 0)- (yyval.blk) = gen_const(jv_true());- else if (strcmp(s, "null") == 0)- (yyval.blk) = gen_const(jv_null());- else- (yyval.blk) = gen_location((yyloc), locations, gen_call(s, gen_noop()));- jv_free((yyvsp[0].literal));-}-#line 3228 "parser.c" /* yacc.c:1646 */- break;-- case 106:-#line 746 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_call(jv_string_value((yyvsp[-3].literal)), (yyvsp[-1].blk));- (yyval.blk) = gen_location((yylsp[-3]), locations, (yyval.blk));- jv_free((yyvsp[-3].literal));-}-#line 3238 "parser.c" /* yacc.c:1646 */- break;-- case 107:-#line 751 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = gen_noop(); }-#line 3244 "parser.c" /* yacc.c:1646 */- break;-- case 108:-#line 752 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = gen_noop(); }-#line 3250 "parser.c" /* yacc.c:1646 */- break;-- case 109:-#line 753 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = (yyvsp[-3].blk); }-#line 3256 "parser.c" /* yacc.c:1646 */- break;-- case 110:-#line 754 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = gen_noop(); }-#line 3262 "parser.c" /* yacc.c:1646 */- break;-- case 111:-#line 757 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 3270 "parser.c" /* yacc.c:1646 */- break;-- case 112:-#line 760 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 3278 "parser.c" /* yacc.c:1646 */- break;-- case 113:-#line 765 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_lambda((yyvsp[0].blk));-}-#line 3286 "parser.c" /* yacc.c:1646 */- break;-- case 114:-#line 770 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal)));- jv_free((yyvsp[0].literal));-}-#line 3295 "parser.c" /* yacc.c:1646 */- break;-- case 115:-#line 774 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP));-}-#line 3303 "parser.c" /* yacc.c:1646 */- break;-- case 116:-#line 777 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-1].blk), gen_op_simple(POP));-}-#line 3311 "parser.c" /* yacc.c:1646 */- break;-- case 117:-#line 782 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_array_matcher(gen_noop(), (yyvsp[0].blk));-}-#line 3319 "parser.c" /* yacc.c:1646 */- break;-- case 118:-#line 785 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_array_matcher((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 3327 "parser.c" /* yacc.c:1646 */- break;-- case 119:-#line 790 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = (yyvsp[0].blk);-}-#line 3335 "parser.c" /* yacc.c:1646 */- break;-- case 120:-#line 793 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = BLOCK((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 3343 "parser.c" /* yacc.c:1646 */- break;-- case 121:-#line 798 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_object_matcher(gen_const((yyvsp[0].literal)), gen_op_unbound(STOREV, jv_string_value((yyvsp[0].literal))));-}-#line 3351 "parser.c" /* yacc.c:1646 */- break;-- case 122:-#line 801 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk));-}-#line 3359 "parser.c" /* yacc.c:1646 */- break;-- case 123:-#line 804 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_object_matcher(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk));-}-#line 3367 "parser.c" /* yacc.c:1646 */- break;-- case 124:-#line 807 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_object_matcher((yyvsp[-2].blk), (yyvsp[0].blk));-}-#line 3375 "parser.c" /* yacc.c:1646 */- break;-- case 125:-#line 810 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_object_matcher((yyvsp[-3].blk), (yyvsp[0].blk));-}-#line 3383 "parser.c" /* yacc.c:1646 */- break;-- case 126:-#line 815 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("as");-}-#line 3391 "parser.c" /* yacc.c:1646 */- break;-- case 127:-#line 818 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("def");-}-#line 3399 "parser.c" /* yacc.c:1646 */- break;-- case 128:-#line 821 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("module");-}-#line 3407 "parser.c" /* yacc.c:1646 */- break;-- case 129:-#line 824 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("import");-}-#line 3415 "parser.c" /* yacc.c:1646 */- break;-- case 130:-#line 827 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("include");-}-#line 3423 "parser.c" /* yacc.c:1646 */- break;-- case 131:-#line 830 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("if");-}-#line 3431 "parser.c" /* yacc.c:1646 */- break;-- case 132:-#line 833 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("then");-}-#line 3439 "parser.c" /* yacc.c:1646 */- break;-- case 133:-#line 836 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("else");-}-#line 3447 "parser.c" /* yacc.c:1646 */- break;-- case 134:-#line 839 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("elif");-}-#line 3455 "parser.c" /* yacc.c:1646 */- break;-- case 135:-#line 842 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("reduce");-}-#line 3463 "parser.c" /* yacc.c:1646 */- break;-- case 136:-#line 845 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("foreach");-}-#line 3471 "parser.c" /* yacc.c:1646 */- break;-- case 137:-#line 848 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("end");-}-#line 3479 "parser.c" /* yacc.c:1646 */- break;-- case 138:-#line 851 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("and");-}-#line 3487 "parser.c" /* yacc.c:1646 */- break;-- case 139:-#line 854 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("or");-}-#line 3495 "parser.c" /* yacc.c:1646 */- break;-- case 140:-#line 857 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("try");-}-#line 3503 "parser.c" /* yacc.c:1646 */- break;-- case 141:-#line 860 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("catch");-}-#line 3511 "parser.c" /* yacc.c:1646 */- break;-- case 142:-#line 863 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("label");-}-#line 3519 "parser.c" /* yacc.c:1646 */- break;-- case 143:-#line 866 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("break");-}-#line 3527 "parser.c" /* yacc.c:1646 */- break;-- case 144:-#line 869 "parser.y" /* yacc.c:1646 */- {- (yyval.literal) = jv_string("__loc__");-}-#line 3535 "parser.c" /* yacc.c:1646 */- break;-- case 145:-#line 874 "parser.y" /* yacc.c:1646 */- {- (yyval.blk)=gen_noop();-}-#line 3543 "parser.c" /* yacc.c:1646 */- break;-- case 146:-#line 877 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = (yyvsp[0].blk); }-#line 3549 "parser.c" /* yacc.c:1646 */- break;-- case 147:-#line 878 "parser.y" /* yacc.c:1646 */- { (yyval.blk)=block_join((yyvsp[-2].blk), (yyvsp[0].blk)); }-#line 3555 "parser.c" /* yacc.c:1646 */- break;-- case 148:-#line 879 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = (yyvsp[0].blk); }-#line 3561 "parser.c" /* yacc.c:1646 */- break;-- case 149:-#line 882 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk));- }-#line 3569 "parser.c" /* yacc.c:1646 */- break;-- case 150:-#line 885 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair(gen_const((yyvsp[-2].literal)), (yyvsp[0].blk));- }-#line 3577 "parser.c" /* yacc.c:1646 */- break;-- case 151:-#line 888 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair((yyvsp[-2].blk), (yyvsp[0].blk));- }-#line 3585 "parser.c" /* yacc.c:1646 */- break;-- case 152:-#line 891 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair((yyvsp[0].blk), BLOCK(gen_op_simple(POP), gen_op_simple(DUP2),- gen_op_simple(DUP2), gen_op_simple(INDEX)));- }-#line 3594 "parser.c" /* yacc.c:1646 */- break;-- case 153:-#line 895 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair(gen_const((yyvsp[0].literal)),- gen_location((yyloc), locations, gen_op_unbound(LOADV, jv_string_value((yyvsp[0].literal)))));- }-#line 3603 "parser.c" /* yacc.c:1646 */- break;-- case 154:-#line 899 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair(gen_const(jv_copy((yyvsp[0].literal))),- gen_index(gen_noop(), gen_const((yyvsp[0].literal))));- }-#line 3612 "parser.c" /* yacc.c:1646 */- break;-- case 155:-#line 903 "parser.y" /* yacc.c:1646 */- {- (yyval.blk) = gen_dictpair((yyvsp[-3].blk), (yyvsp[0].blk));- }-#line 3620 "parser.c" /* yacc.c:1646 */- break;-- case 156:-#line 906 "parser.y" /* yacc.c:1646 */- { (yyval.blk) = (yyvsp[0].blk); }-#line 3626 "parser.c" /* yacc.c:1646 */- break;---#line 3630 "parser.c" /* yacc.c:1646 */- default: break;- }- /* User semantic actions sometimes alter yychar, and that requires- that yytoken be updated with the new translation. We take the- approach of translating immediately before every use of yytoken.- One alternative is translating here after every semantic action,- but that translation would be missed if the semantic action invokes- YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or- if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an- incorrect destructor might then be invoked immediately. In the- case of YYERROR or YYBACKUP, subsequent parser actions might lead- to an incorrect destructor call or verbose syntax error message- before the lookahead is translated. */- YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);-- YYPOPSTACK (yylen);- yylen = 0;- YY_STACK_PRINT (yyss, yyssp);-- *++yyvsp = yyval;- *++yylsp = yyloc;-- /* Now 'shift' the result of the reduction. Determine what state- that goes to, based on the state we popped back to and the rule- number reduced by. */-- yyn = yyr1[yyn];-- yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;- if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)- yystate = yytable[yystate];- else- yystate = yydefgoto[yyn - YYNTOKENS];-- goto yynewstate;---/*--------------------------------------.-| yyerrlab -- here on detecting error. |-`--------------------------------------*/-yyerrlab:- /* Make sure we have latest lookahead translation. See comments at- user semantic actions for why this is necessary. */- yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);-- /* If not already recovering from an error, report this error. */- if (!yyerrstatus)- {- ++yynerrs;-#if ! YYERROR_VERBOSE- yyerror (&yylloc, answer, errors, locations, lexer_param_ptr, YY_("syntax error"));-#else-# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \- yyssp, yytoken)- {- char const *yymsgp = YY_("syntax error");- int yysyntax_error_status;- yysyntax_error_status = YYSYNTAX_ERROR;- if (yysyntax_error_status == 0)- yymsgp = yymsg;- else if (yysyntax_error_status == 1)- {- if (yymsg != yymsgbuf)- YYSTACK_FREE (yymsg);- yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);- if (!yymsg)- {- yymsg = yymsgbuf;- yymsg_alloc = sizeof yymsgbuf;- yysyntax_error_status = 2;- }- else- {- yysyntax_error_status = YYSYNTAX_ERROR;- yymsgp = yymsg;- }- }- yyerror (&yylloc, answer, errors, locations, lexer_param_ptr, yymsgp);- if (yysyntax_error_status == 2)- goto yyexhaustedlab;- }-# undef YYSYNTAX_ERROR-#endif- }-- yyerror_range[1] = yylloc;-- if (yyerrstatus == 3)- {- /* If just tried and failed to reuse lookahead token after an- error, discard it. */-- if (yychar <= YYEOF)- {- /* Return failure if at end of input. */- if (yychar == YYEOF)- YYABORT;- }- else- {- yydestruct ("Error: discarding",- yytoken, &yylval, &yylloc, answer, errors, locations, lexer_param_ptr);- yychar = YYEMPTY;- }- }-- /* Else will try to reuse lookahead token after shifting the error- token. */- goto yyerrlab1;---/*---------------------------------------------------.-| yyerrorlab -- error raised explicitly by YYERROR. |-`---------------------------------------------------*/-yyerrorlab:-- /* Pacify compilers like GCC when the user code never invokes- YYERROR and the label yyerrorlab therefore never appears in user- code. */- if (/*CONSTCOND*/ 0)- goto yyerrorlab;-- yyerror_range[1] = yylsp[1-yylen];- /* Do not reclaim the symbols of the rule whose action triggered- this YYERROR. */- YYPOPSTACK (yylen);- yylen = 0;- YY_STACK_PRINT (yyss, yyssp);- yystate = *yyssp;- goto yyerrlab1;---/*-------------------------------------------------------------.-| yyerrlab1 -- common code for both syntax error and YYERROR. |-`-------------------------------------------------------------*/-yyerrlab1:- yyerrstatus = 3; /* Each real token shifted decrements this. */-- for (;;)- {- yyn = yypact[yystate];- if (!yypact_value_is_default (yyn))- {- yyn += YYTERROR;- if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)- {- yyn = yytable[yyn];- if (0 < yyn)- break;- }- }-- /* Pop the current state because it cannot handle the error token. */- if (yyssp == yyss)- YYABORT;-- yyerror_range[1] = *yylsp;- yydestruct ("Error: popping",- yystos[yystate], yyvsp, yylsp, answer, errors, locations, lexer_param_ptr);- YYPOPSTACK (1);- yystate = *yyssp;- YY_STACK_PRINT (yyss, yyssp);- }-- YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN- *++yyvsp = yylval;- YY_IGNORE_MAYBE_UNINITIALIZED_END-- yyerror_range[2] = yylloc;- /* Using YYLLOC is tempting, but would change the location of- the lookahead. YYLOC is available though. */- YYLLOC_DEFAULT (yyloc, yyerror_range, 2);- *++yylsp = yyloc;-- /* Shift the error token. */- YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);-- yystate = yyn;- goto yynewstate;---/*-------------------------------------.-| yyacceptlab -- YYACCEPT comes here. |-`-------------------------------------*/-yyacceptlab:- yyresult = 0;- goto yyreturn;--/*-----------------------------------.-| yyabortlab -- YYABORT comes here. |-`-----------------------------------*/-yyabortlab:- yyresult = 1;- goto yyreturn;--#if !defined yyoverflow || YYERROR_VERBOSE-/*-------------------------------------------------.-| yyexhaustedlab -- memory exhaustion comes here. |-`-------------------------------------------------*/-yyexhaustedlab:- yyerror (&yylloc, answer, errors, locations, lexer_param_ptr, YY_("memory exhausted"));- yyresult = 2;- /* Fall through. */-#endif--yyreturn:- if (yychar != YYEMPTY)- {- /* Make sure we have latest lookahead translation. See comments at- user semantic actions for why this is necessary. */- yytoken = YYTRANSLATE (yychar);- yydestruct ("Cleanup: discarding lookahead",- yytoken, &yylval, &yylloc, answer, errors, locations, lexer_param_ptr);- }- /* Do not reclaim the symbols of the rule whose action triggered- this YYABORT or YYACCEPT. */- YYPOPSTACK (yylen);- YY_STACK_PRINT (yyss, yyssp);- while (yyssp != yyss)- {- yydestruct ("Cleanup: popping",- yystos[*yyssp], yyvsp, yylsp, answer, errors, locations, lexer_param_ptr);- YYPOPSTACK (1);- }-#ifndef yyoverflow- if (yyss != yyssa)- YYSTACK_FREE (yyss);-#endif-#if YYERROR_VERBOSE- if (yymsg != yymsgbuf)- YYSTACK_FREE (yymsg);-#endif- return yyresult;-}-#line 907 "parser.y" /* yacc.c:1906 */---int jq_parse(struct locfile* locations, block* answer) {- struct lexer_param scanner;- YY_BUFFER_STATE buf;- jq_yylex_init_extra(0, &scanner.lexer);- buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer);- int errors = 0;- *answer = gen_noop();- yyparse(answer, &errors, locations, &scanner);- jq_yy_delete_buffer(buf, scanner.lexer);- jq_yylex_destroy(scanner.lexer);- if (errors > 0) {- block_free(*answer);- *answer = gen_noop();- }- return errors;-}--int jq_parse_library(struct locfile* locations, block* answer) {- int errs = jq_parse(locations, answer);- if (errs) return errs;- if (block_has_main(*answer)) {- locfile_locate(locations, UNKNOWN_LOCATION, "jq: error: library should only have function definitions, not a main expression");- return 1;- }- assert(block_has_only_binders_and_imports(*answer, OP_IS_CALL_PSEUDO));- return 0;-}
parser.h
@@ -1,193 +0,0 @@-/* A Bison parser, made by GNU Bison 3.0.2. */--/* Bison interface for Yacc-like parsers in C-- Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.-- This program is free software: you can redistribute it and/or modify- it under the terms of the GNU General Public License as published by- the Free Software Foundation, either version 3 of the License, or- (at your option) any later version.-- This program is distributed in the hope that it will be useful,- but WITHOUT ANY WARRANTY; without even the implied warranty of- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the- GNU General Public License for more details.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>. */--/* As a special exception, you may create a larger work that contains- part or all of the Bison parser skeleton and distribute that work- under terms of your choice, so long as that work isn't itself a- parser generator using the skeleton or a modified version thereof- as a parser skeleton. Alternatively, if you modify or redistribute- the parser skeleton itself, you may (at your option) remove this- special exception, which will cause the skeleton and the resulting- Bison output files to be licensed under the GNU General Public- License without this special exception.-- This special exception was added by the Free Software Foundation in- version 2.2 of Bison. */--#ifndef YY_YY_PARSER_H_INCLUDED-# define YY_YY_PARSER_H_INCLUDED-/* Debug traces. */-#ifndef YYDEBUG-# define YYDEBUG 0-#endif-#if YYDEBUG-extern int yydebug;-#endif-/* "%code requires" blocks. */-#line 11 "parser.y" /* yacc.c:1909 */--#include "locfile.h"-struct lexer_param;--#define YYLTYPE location-#define YYLLOC_DEFAULT(Loc, Rhs, N) \- do { \- if (N) { \- (Loc).start = YYRHSLOC(Rhs, 1).start; \- (Loc).end = YYRHSLOC(Rhs, N).end; \- } else { \- (Loc).start = YYRHSLOC(Rhs, 0).end; \- (Loc).end = YYRHSLOC(Rhs, 0).end; \- } \- } while (0)--#line 61 "parser.h" /* yacc.c:1909 */--/* Token type. */-#ifndef YYTOKENTYPE-# define YYTOKENTYPE- enum yytokentype- {- INVALID_CHARACTER = 258,- IDENT = 259,- FIELD = 260,- LITERAL = 261,- FORMAT = 262,- REC = 263,- SETMOD = 264,- EQ = 265,- NEQ = 266,- DEFINEDOR = 267,- AS = 268,- DEF = 269,- MODULE = 270,- IMPORT = 271,- INCLUDE = 272,- IF = 273,- THEN = 274,- ELSE = 275,- ELSE_IF = 276,- REDUCE = 277,- FOREACH = 278,- END = 279,- AND = 280,- OR = 281,- TRY = 282,- CATCH = 283,- LABEL = 284,- BREAK = 285,- LOC = 286,- SETPIPE = 287,- SETPLUS = 288,- SETMINUS = 289,- SETMULT = 290,- SETDIV = 291,- SETDEFINEDOR = 292,- LESSEQ = 293,- GREATEREQ = 294,- QQSTRING_START = 295,- QQSTRING_TEXT = 296,- QQSTRING_INTERP_START = 297,- QQSTRING_INTERP_END = 298,- QQSTRING_END = 299,- FUNCDEF = 300,- NONOPT = 301- };-#endif-/* Tokens. */-#define INVALID_CHARACTER 258-#define IDENT 259-#define FIELD 260-#define LITERAL 261-#define FORMAT 262-#define REC 263-#define SETMOD 264-#define EQ 265-#define NEQ 266-#define DEFINEDOR 267-#define AS 268-#define DEF 269-#define MODULE 270-#define IMPORT 271-#define INCLUDE 272-#define IF 273-#define THEN 274-#define ELSE 275-#define ELSE_IF 276-#define REDUCE 277-#define FOREACH 278-#define END 279-#define AND 280-#define OR 281-#define TRY 282-#define CATCH 283-#define LABEL 284-#define BREAK 285-#define LOC 286-#define SETPIPE 287-#define SETPLUS 288-#define SETMINUS 289-#define SETMULT 290-#define SETDIV 291-#define SETDEFINEDOR 292-#define LESSEQ 293-#define GREATEREQ 294-#define QQSTRING_START 295-#define QQSTRING_TEXT 296-#define QQSTRING_INTERP_START 297-#define QQSTRING_INTERP_END 298-#define QQSTRING_END 299-#define FUNCDEF 300-#define NONOPT 301--/* Value type. */-#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED-typedef union YYSTYPE YYSTYPE;-union YYSTYPE-{-#line 31 "parser.y" /* yacc.c:1909 */-- jv literal;- block blk;--#line 170 "parser.h" /* yacc.c:1909 */-};-# define YYSTYPE_IS_TRIVIAL 1-# define YYSTYPE_IS_DECLARED 1-#endif--/* Location type. */-#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED-typedef struct YYLTYPE YYLTYPE;-struct YYLTYPE-{- int first_line;- int first_column;- int last_line;- int last_column;-};-# define YYLTYPE_IS_DECLARED 1-# define YYLTYPE_IS_TRIVIAL 1-#endif----int yyparse (block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr);--#endif /* !YY_YY_PARSER_H_INCLUDED */
parser.y
@@ -1,935 +0,0 @@-%{-#include <assert.h>-#include <math.h>-#include <stdio.h>-#include <string.h>-#include "compile.h"-#include "jv_alloc.h"-#define YYMALLOC jv_mem_alloc-#define YYFREE jv_mem_free-%}-%code requires {-#include "locfile.h"-struct lexer_param;--#define YYLTYPE location-#define YYLLOC_DEFAULT(Loc, Rhs, N) \- do { \- if (N) { \- (Loc).start = YYRHSLOC(Rhs, 1).start; \- (Loc).end = YYRHSLOC(Rhs, N).end; \- } else { \- (Loc).start = YYRHSLOC(Rhs, 0).end; \- (Loc).end = YYRHSLOC(Rhs, 0).end; \- } \- } while (0)-}--%locations-%error-verbose-%define api.pure-%union {- jv literal;- block blk;-}--%destructor { jv_free($$); } <literal>-%destructor { block_free($$); } <blk>--%parse-param {block* answer}-%parse-param {int* errors}-%parse-param {struct locfile* locations}-%parse-param {struct lexer_param* lexer_param_ptr}-%lex-param {block* answer}-%lex-param {int* errors}-%lex-param {struct locfile* locations}-%lex-param {struct lexer_param* lexer_param_ptr}---%token INVALID_CHARACTER-%token <literal> IDENT-%token <literal> FIELD-%token <literal> LITERAL-%token <literal> FORMAT-%token REC ".."-%token SETMOD "%="-%token EQ "=="-%token NEQ "!="-%token DEFINEDOR "//"-%token AS "as"-%token DEF "def"-%token MODULE "module"-%token IMPORT "import"-%token INCLUDE "include"-%token IF "if"-%token THEN "then"-%token ELSE "else"-%token ELSE_IF "elif"-%token REDUCE "reduce"-%token FOREACH "foreach"-%token END "end"-%token AND "and"-%token OR "or"-%token TRY "try"-%token CATCH "catch"-%token LABEL "label"-%token BREAK "break"-%token LOC "__loc__"-%token SETPIPE "|="-%token SETPLUS "+="-%token SETMINUS "-="-%token SETMULT "*="-%token SETDIV "/="-%token SETDEFINEDOR "//="-%token LESSEQ "<="-%token GREATEREQ ">="--%token QQSTRING_START-%token <literal> QQSTRING_TEXT-%token QQSTRING_INTERP_START-%token QQSTRING_INTERP_END-%token QQSTRING_END--/* Instead of raising this, find a way to use precedence to resolve- * shift-reduce conflicts. */-%expect 0--%precedence FUNCDEF-%right '|'-%left ','-%right "//"-%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETMOD SETDEFINEDOR-%left OR-%left AND-%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ-%left '+' '-'-%left '*' '/' '%'-%precedence NONOPT /* non-optional; rules for which a specialized- '?' rule should be preferred over Exp '?' */-%precedence '?'-%precedence "try"-%precedence "catch"---%type <blk> Exp Term-%type <blk> MkDict MkDictPair ExpD-%type <blk> ElseBody-%type <blk> String QQString-%type <blk> FuncDef FuncDefs-%type <blk> Module Import Imports ImportWhat ImportFrom-%type <blk> Param Params Arg Args-%type <blk> Pattern ArrayPats ObjPats ObjPat-%type <literal> Keyword-%{-#include "lexer.h"-struct lexer_param {- yyscan_t lexer;-};-#define FAIL(loc, msg) \- do { \- location l = loc; \- yyerror(&l, answer, errors, locations, lexer_param_ptr, msg); \- /*YYERROR*/; \- } while (0)--void yyerror(YYLTYPE* loc, block* answer, int* errors,- struct locfile* locations, struct lexer_param* lexer_param_ptr, const char *s){- (*errors)++;- if (strstr(s, "unexpected")) {-#ifdef WIN32- locfile_locate(locations, *loc, "jq: error: %s (Windows cmd shell quoting issues?)", s);-#else- locfile_locate(locations, *loc, "jq: error: %s (Unix shell quoting issues?)", s);-#endif- } else {- locfile_locate(locations, *loc, "jq: error: %s", s);- }-}--int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,- struct locfile* locations, struct lexer_param* lexer_param_ptr) {- yyscan_t lexer = lexer_param_ptr->lexer;- int tok = jq_yylex(yylval, yylloc, lexer);- if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {- jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));- if (jv_get_kind(msg) == JV_KIND_STRING) {- FAIL(*yylloc, jv_string_value(msg));- } else {- FAIL(*yylloc, "Invalid literal");- }- jv_free(msg);- jv_free(yylval->literal);- yylval->literal = jv_null();- }- return tok;-}--static block gen_dictpair(block k, block v) {- return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT));-}--static block gen_index(block obj, block key) {- return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));-}--static block gen_index_opt(block obj, block key) {- return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT));-}--static block gen_slice_index(block obj, block start, block end, opcode idx_op) {- block key = BLOCK(gen_subexp(gen_const(jv_object())),- gen_subexp(gen_const(jv_string("start"))),- gen_subexp(start),- gen_op_simple(INSERT),- gen_subexp(gen_const(jv_string("end"))),- gen_subexp(end),- gen_op_simple(INSERT));- return BLOCK(key, obj, gen_op_simple(idx_op));-}--static block constant_fold(block a, block b, int op) {- if (!block_is_single(a) || !block_is_const(a) ||- !block_is_single(b) || !block_is_const(b))- return gen_noop();- if (op == '+') {- if (block_const_kind(a) == JV_KIND_NULL) {- block_free(a);- return b;- }- if (block_const_kind(b) == JV_KIND_NULL) {- block_free(b);- return a;- }- }- if (block_const_kind(a) != block_const_kind(b))- return gen_noop();-- jv res = jv_invalid();-- if (block_const_kind(a) == JV_KIND_NUMBER) {- double na = jv_number_value(block_const(a));- double nb = jv_number_value(block_const(b));- switch (op) {- case '+': res = jv_number(na + nb); break;- case '-': res = jv_number(na - nb); break;- case '*': res = jv_number(na * nb); break;- case '/': res = jv_number(na / nb); break;- case EQ: res = (na == nb ? jv_true() : jv_false()); break;- case NEQ: res = (na != nb ? jv_true() : jv_false()); break;- case '<': res = (na < nb ? jv_true() : jv_false()); break;- case '>': res = (na > nb ? jv_true() : jv_false()); break;- case LESSEQ: res = (na <= nb ? jv_true() : jv_false()); break;- case GREATEREQ: res = (na >= nb ? jv_true() : jv_false()); break;- default: break;- }- } else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) {- res = jv_string_concat(block_const(a), block_const(b));- } else {- return gen_noop();- }-- if (jv_get_kind(res) == JV_KIND_INVALID)- return gen_noop();-- block_free(a);- block_free(b);- return gen_const(res);-}--static block gen_binop(block a, block b, int op) {- block folded = constant_fold(a, b, op);- if (!block_is_noop(folded))- return folded;-- const char* funcname = 0;- switch (op) {- case '+': funcname = "_plus"; break;- case '-': funcname = "_minus"; break;- case '*': funcname = "_multiply"; break;- case '/': funcname = "_divide"; break;- case '%': funcname = "_mod"; break;- case EQ: funcname = "_equal"; break;- case NEQ: funcname = "_notequal"; break;- case '<': funcname = "_less"; break;- case '>': funcname = "_greater"; break;- case LESSEQ: funcname = "_lesseq"; break;- case GREATEREQ: funcname = "_greatereq"; break;- }- assert(funcname);-- return gen_call(funcname, BLOCK(gen_lambda(a), gen_lambda(b)));-}--static block gen_format(block a, jv fmt) {- return BLOCK(a, gen_call("format", gen_lambda(gen_const(fmt))));-}--static block gen_definedor_assign(block object, block val) {- block tmp = gen_op_var_fresh(STOREV, "tmp");- return BLOCK(gen_op_simple(DUP),- val, tmp,- gen_call("_modify", BLOCK(gen_lambda(object),- gen_lambda(gen_definedor(gen_noop(),- gen_op_bound(LOADV, tmp))))));-}--static block gen_update(block object, block val, int optype) {- block tmp = gen_op_var_fresh(STOREV, "tmp");- return BLOCK(gen_op_simple(DUP),- val,- tmp,- gen_call("_modify", BLOCK(gen_lambda(object),- gen_lambda(gen_binop(gen_noop(),- gen_op_bound(LOADV, tmp),- optype)))));-}--%}--%%-TopLevel:-Module Imports Exp {- *answer = BLOCK($1, $2, gen_op_simple(TOP), $3);-} |-Module Imports FuncDefs {- *answer = BLOCK($1, $2, $3);-}--Module:-%empty {- $$ = gen_noop();-} |-"module" Exp ';' {- if (!block_is_const($2)) {- FAIL(@$, "Module metadata must be constant");- $$ = gen_noop();- block_free($2);- } else {- $$ = gen_module($2);- }-}--Imports:-%empty {- $$ = gen_noop();-} |-Import Imports {- $$ = BLOCK($1, $2);-}--FuncDefs:-%empty {- $$ = gen_noop();-} |-FuncDef FuncDefs {- $$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);-}--Exp:-FuncDef Exp %prec FUNCDEF {- $$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO);-} |--Term "as" Pattern '|' Exp {- $$ = gen_destructure($1, $3, $5);-} |--"reduce" Term "as" Pattern '(' Exp ';' Exp ')' {- $$ = gen_reduce($2, $4, $6, $8);-} |--"foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' {- $$ = gen_foreach($2, $4, $6, $8, $10);-} |--"foreach" Term "as" Pattern '(' Exp ';' Exp ')' {- $$ = gen_foreach($2, $4, $6, $8, gen_noop());-} |--"if" Exp "then" Exp ElseBody {- $$ = gen_cond($2, $4, $5);-} |-"if" Exp "then" error {- FAIL(@$, "Possibly unterminated 'if' statement");- $$ = $2;-} |--"try" Exp "catch" Exp {- //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, $4);- $$ = gen_try($2, gen_try_handler($4));-} |-"try" Exp {- //$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, gen_op_simple(BACKTRACK));- $$ = gen_try($2, gen_op_simple(BACKTRACK));-} |-"try" Exp "catch" error {- FAIL(@$, "Possibly unterminated 'try' statement");- $$ = $2;-} |--"label" '$' IDENT '|' Exp {- jv v = jv_string_fmt("*label-%s", jv_string_value($3));- $$ = gen_location(@$, locations, gen_label(jv_string_value(v), $5));- jv_free($3);- jv_free(v);-} |--Exp '?' {- $$ = gen_try($1, gen_op_simple(BACKTRACK));-} |--Exp '=' Exp {- $$ = gen_call("_assign", BLOCK(gen_lambda($1), gen_lambda($3)));-} |--Exp "or" Exp {- $$ = gen_or($1, $3);-} |--Exp "and" Exp {- $$ = gen_and($1, $3);-} |--Exp "//" Exp {- $$ = gen_definedor($1, $3);-} |--Exp "//=" Exp {- $$ = gen_definedor_assign($1, $3);-} |--Exp "|=" Exp {- $$ = gen_call("_modify", BLOCK(gen_lambda($1), gen_lambda($3)));-} |--Exp '|' Exp {- $$ = block_join($1, $3);-} |--Exp ',' Exp {- $$ = gen_both($1, $3);-} |--Exp '+' Exp {- $$ = gen_binop($1, $3, '+');-} |--Exp "+=" Exp {- $$ = gen_update($1, $3, '+');-} |--'-' Exp {- $$ = BLOCK($2, gen_call("_negate", gen_noop()));-} |--Exp '-' Exp {- $$ = gen_binop($1, $3, '-');-} |--Exp "-=" Exp {- $$ = gen_update($1, $3, '-');-} |--Exp '*' Exp {- $$ = gen_binop($1, $3, '*');-} |--Exp "*=" Exp {- $$ = gen_update($1, $3, '*');-} |--Exp '/' Exp {- $$ = gen_binop($1, $3, '/');- if (block_is_const_inf($$))- FAIL(@$, "Division by zero?");-} |--Exp '%' Exp {- $$ = gen_binop($1, $3, '%');- if (block_is_const_inf($$))- FAIL(@$, "Remainder by zero?");-} |--Exp "/=" Exp {- $$ = gen_update($1, $3, '/');-} |--Exp SETMOD Exp {- $$ = gen_update($1, $3, '%');-} |--Exp "==" Exp {- $$ = gen_binop($1, $3, EQ);-} |--Exp "!=" Exp {- $$ = gen_binop($1, $3, NEQ);-} |--Exp '<' Exp {- $$ = gen_binop($1, $3, '<');-} |--Exp '>' Exp {- $$ = gen_binop($1, $3, '>');-} |--Exp "<=" Exp {- $$ = gen_binop($1, $3, LESSEQ);-} |--Exp ">=" Exp {- $$ = gen_binop($1, $3, GREATEREQ);-} |--Term {- $$ = $1;-}--Import:-ImportWhat ';' {- $$ = $1;-} |-ImportWhat Exp ';' {- if (!block_is_const($2)) {- FAIL(@$, "Module metadata must be constant");- $$ = gen_noop();- block_free($1);- block_free($2);- } else if (block_const_kind($2) != JV_KIND_OBJECT) {- FAIL(@$, "Module metadata must be an object");- $$ = gen_noop();- block_free($1);- block_free($2);- } else {- $$ = gen_import_meta($1, $2);- }-}--ImportWhat:-"import" ImportFrom "as" '$' IDENT {- jv v = block_const($2);- // XXX Make gen_import take only blocks and the int is_data so we- // don't have to free so much stuff here- $$ = gen_import(jv_string_value(v), jv_string_value($5), 1);- block_free($2);- jv_free($5);- jv_free(v);-} |-"import" ImportFrom "as" IDENT {- jv v = block_const($2);- $$ = gen_import(jv_string_value(v), jv_string_value($4), 0);- block_free($2);- jv_free($4);- jv_free(v);-} |-"include" ImportFrom {- jv v = block_const($2);- $$ = gen_import(jv_string_value(v), NULL, 0);- block_free($2);- jv_free(v);-}--ImportFrom:-String {- if (!block_is_const($1)) {- FAIL(@$, "Import path must be constant");- $$ = gen_const(jv_string(""));- block_free($1);- } else {- $$ = $1;- }-}--FuncDef:-"def" IDENT ':' Exp ';' {- $$ = gen_function(jv_string_value($2), gen_noop(), $4);- jv_free($2);-} |--"def" IDENT '(' Params ')' ':' Exp ';' {- $$ = gen_function(jv_string_value($2), $4, $7);- jv_free($2);-}--Params:-Param {- $$ = $1;-} |-Params ';' Param {- $$ = BLOCK($1, $3);-}--Param:-'$' IDENT {- $$ = gen_param_regular(jv_string_value($2));- jv_free($2);-} |--IDENT {- $$ = gen_param(jv_string_value($1));- jv_free($1);-}---String:-QQSTRING_START { $<literal>$ = jv_string("text"); } QQString QQSTRING_END {- $$ = $3;- jv_free($<literal>2);-} |-FORMAT QQSTRING_START { $<literal>$ = $1; } QQString QQSTRING_END {- $$ = $4;- jv_free($<literal>3);-}---QQString:-%empty {- $$ = gen_const(jv_string(""));-} |-QQString QQSTRING_TEXT {- $$ = gen_binop($1, gen_const($2), '+');-} |-QQString QQSTRING_INTERP_START Exp QQSTRING_INTERP_END {- $$ = gen_binop($1, gen_format($3, jv_copy($<literal>0)), '+');-}---ElseBody:-"elif" Exp "then" Exp ElseBody {- $$ = gen_cond($2, $4, $5);-} |-"else" Exp "end" {- $$ = $2;-}--ExpD:-ExpD '|' ExpD {- $$ = block_join($1, $3);-} |-'-' ExpD {- $$ = BLOCK($2, gen_call("_negate", gen_noop()));-} |-Term {- $$ = $1;-}---Term:-'.' {- $$ = gen_noop();-} |-REC {- $$ = gen_call("recurse", gen_noop());-} |-BREAK '$' IDENT {- jv v = jv_string_fmt("*label-%s", jv_string_value($3)); // impossible symbol- $$ = gen_location(@$, locations,- BLOCK(gen_op_unbound(LOADV, jv_string_value(v)),- gen_call("error", gen_noop())));- jv_free(v);- jv_free($3);-} |-BREAK error {- FAIL(@$, "break requires a label to break to");- $$ = gen_noop();-} |-Term FIELD '?' {- $$ = gen_index_opt($1, gen_const($2));-} |-FIELD '?' {- $$ = gen_index_opt(gen_noop(), gen_const($1));-} |-Term '.' String '?' {- $$ = gen_index_opt($1, $3);-} |-'.' String '?' {- $$ = gen_index_opt(gen_noop(), $2);-} |-Term FIELD %prec NONOPT {- $$ = gen_index($1, gen_const($2));-} |-FIELD %prec NONOPT {- $$ = gen_index(gen_noop(), gen_const($1));-} |-Term '.' String %prec NONOPT {- $$ = gen_index($1, $3);-} |-'.' String %prec NONOPT {- $$ = gen_index(gen_noop(), $2);-} |-'.' error {- FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");- $$ = gen_noop();-} |-'.' IDENT error {- jv_free($2);- FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");- $$ = gen_noop();-} |-/* FIXME: string literals */-Term '[' Exp ']' '?' {- $$ = gen_index_opt($1, $3);-} |-Term '[' Exp ']' %prec NONOPT {- $$ = gen_index($1, $3);-} |-Term '[' ']' '?' {- $$ = block_join($1, gen_op_simple(EACH_OPT));-} |-Term '[' ']' %prec NONOPT {- $$ = block_join($1, gen_op_simple(EACH));-} |-Term '[' Exp ':' Exp ']' '?' {- $$ = gen_slice_index($1, $3, $5, INDEX_OPT);-} |-Term '[' Exp ':' ']' '?' {- $$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX_OPT);-} |-Term '[' ':' Exp ']' '?' {- $$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX_OPT);-} |-Term '[' Exp ':' Exp ']' %prec NONOPT {- $$ = gen_slice_index($1, $3, $5, INDEX);-} |-Term '[' Exp ':' ']' %prec NONOPT {- $$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX);-} |-Term '[' ':' Exp ']' %prec NONOPT {- $$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX);-} |-LITERAL {- $$ = gen_const($1);-} |-String {- $$ = $1;-} |-FORMAT {- $$ = gen_format(gen_noop(), $1);-} |-'(' Exp ')' {- $$ = $2;-} |-'[' Exp ']' {- $$ = gen_collect($2);-} |-'[' ']' {- $$ = gen_const(jv_array());-} |-'{' MkDict '}' {- block o = gen_const_object($2);- if (o.first != NULL)- $$ = o;- else- $$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP));-} |-'$' LOC {- $$ = gen_const(JV_OBJECT(jv_string("file"), jv_copy(locations->fname),- jv_string("line"), jv_number(locfile_get_line(locations, @$.start) + 1)));-} |-'$' IDENT {- $$ = gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2)));- jv_free($2);-} |-IDENT {- const char *s = jv_string_value($1);- if (strcmp(s, "false") == 0)- $$ = gen_const(jv_false());- else if (strcmp(s, "true") == 0)- $$ = gen_const(jv_true());- else if (strcmp(s, "null") == 0)- $$ = gen_const(jv_null());- else- $$ = gen_location(@$, locations, gen_call(s, gen_noop()));- jv_free($1);-} |-IDENT '(' Args ')' {- $$ = gen_call(jv_string_value($1), $3);- $$ = gen_location(@1, locations, $$);- jv_free($1);-} |-'(' error ')' { $$ = gen_noop(); } |-'[' error ']' { $$ = gen_noop(); } |-Term '[' error ']' { $$ = $1; } |-'{' error '}' { $$ = gen_noop(); }--Args:-Arg {- $$ = $1;-} |-Args ';' Arg {- $$ = BLOCK($1, $3);-}--Arg:-Exp {- $$ = gen_lambda($1);-}--Pattern:-'$' IDENT {- $$ = gen_op_unbound(STOREV, jv_string_value($2));- jv_free($2);-} |-'[' ArrayPats ']' {- $$ = BLOCK($2, gen_op_simple(POP));-} |-'{' ObjPats '}' {- $$ = BLOCK($2, gen_op_simple(POP));-}--ArrayPats:-Pattern {- $$ = gen_array_matcher(gen_noop(), $1);-} |-ArrayPats ',' Pattern {- $$ = gen_array_matcher($1, $3);-}--ObjPats:-ObjPat {- $$ = $1;-} |-ObjPats ',' ObjPat {- $$ = BLOCK($1, $3);-}--ObjPat:-'$' IDENT {- $$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value($2)));-} |-IDENT ':' Pattern {- $$ = gen_object_matcher(gen_const($1), $3);-} |-Keyword ':' Pattern {- $$ = gen_object_matcher(gen_const($1), $3);-} |-String ':' Pattern {- $$ = gen_object_matcher($1, $3);-} |-'(' Exp ')' ':' Pattern {- $$ = gen_object_matcher($2, $5);-}--Keyword:-"as" {- $$ = jv_string("as");-} |-"def" {- $$ = jv_string("def");-} |-"module" {- $$ = jv_string("module");-} |-"import" {- $$ = jv_string("import");-} |-"include" {- $$ = jv_string("include");-} |-"if" {- $$ = jv_string("if");-} |-"then" {- $$ = jv_string("then");-} |-"else" {- $$ = jv_string("else");-} |-"elif" {- $$ = jv_string("elif");-} |-"reduce" {- $$ = jv_string("reduce");-} |-"foreach" {- $$ = jv_string("foreach");-} |-"end" {- $$ = jv_string("end");-} |-"and" {- $$ = jv_string("and");-} |-"or" {- $$ = jv_string("or");-} |-"try" {- $$ = jv_string("try");-} |-"catch" {- $$ = jv_string("catch");-} |-"label" {- $$ = jv_string("label");-} |-"break" {- $$ = jv_string("break");-} |-"__loc__" {- $$ = jv_string("__loc__");-}--MkDict:-%empty {- $$=gen_noop();-} |- MkDictPair { $$ = $1; }-| MkDictPair ',' MkDict { $$=block_join($1, $3); }-| error ',' MkDict { $$ = $3; }--MkDictPair:-IDENT ':' ExpD {- $$ = gen_dictpair(gen_const($1), $3);- }-| Keyword ':' ExpD {- $$ = gen_dictpair(gen_const($1), $3);- }-| String ':' ExpD {- $$ = gen_dictpair($1, $3);- }-| String {- $$ = gen_dictpair($1, BLOCK(gen_op_simple(POP), gen_op_simple(DUP2),- gen_op_simple(DUP2), gen_op_simple(INDEX)));- }-| '$' IDENT {- $$ = gen_dictpair(gen_const($2),- gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2))));- }-| IDENT {- $$ = gen_dictpair(gen_const(jv_copy($1)),- gen_index(gen_noop(), gen_const($1)));- }-| '(' Exp ')' ':' ExpD {- $$ = gen_dictpair($2, $5);- }-| '(' error ')' ':' ExpD { $$ = $5; }-%%--int jq_parse(struct locfile* locations, block* answer) {- struct lexer_param scanner;- YY_BUFFER_STATE buf;- jq_yylex_init_extra(0, &scanner.lexer);- buf = jq_yy_scan_bytes(locations->data, locations->length, scanner.lexer);- int errors = 0;- *answer = gen_noop();- yyparse(answer, &errors, locations, &scanner);- jq_yy_delete_buffer(buf, scanner.lexer);- jq_yylex_destroy(scanner.lexer);- if (errors > 0) {- block_free(*answer);- *answer = gen_noop();- }- return errors;-}--int jq_parse_library(struct locfile* locations, block* answer) {- int errs = jq_parse(locations, answer);- if (errs) return errs;- if (block_has_main(*answer)) {- locfile_locate(locations, UNKNOWN_LOCATION, "jq: error: library should only have function definitions, not a main expression");- return 1;- }- assert(block_has_only_binders_and_imports(*answer, OP_IS_CALL_PSEUDO));- return 0;-}
@@ -0,0 +1,1701 @@+#define _BSD_SOURCE+#define _GNU_SOURCE+#define _XOPEN_SOURCE+#include <sys/time.h>+#include <stdlib.h>+#include <stddef.h>+#ifdef HAVE_ALLOCA_H+# include <alloca.h>+#elif !defined alloca+# ifdef __GNUC__+# define alloca __builtin_alloca+# elif defined _MSC_VER+# include <malloc.h>+# define alloca _alloca+# elif !defined HAVE_ALLOCA+# ifdef __cplusplus+extern "C"+# endif+void *alloca (size_t);+# endif+#endif+#include <assert.h>+#include <ctype.h>+#include <limits.h>+#include <math.h>+#ifdef HAVE_ONIGURUMA+#include <oniguruma.h>+#endif+#include <string.h>+#include <time.h>+#include "builtin.h"+#include "compile.h"+#include "jq_parser.h"+#include "bytecode.h"+#include "linker.h"+#include "locfile.h"+#include "jv_unicode.h"+++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 jv type_error2(jv bad1, jv bad2, const char* msg) {+ char errbuf1[15],errbuf2[15];+ jv err = jv_invalid_with_msg(jv_string_fmt("%s (%s) and %s (%s) %s",+ jv_kind_name(jv_get_kind(bad1)),+ jv_dump_string_trunc(jv_copy(bad1), errbuf1, sizeof(errbuf1)),+ jv_kind_name(jv_get_kind(bad2)),+ jv_dump_string_trunc(jv_copy(bad2), errbuf2, sizeof(errbuf2)),+ msg));+ jv_free(bad1);+ jv_free(bad2);+ return err;+}++static jv f_plus(jq_state *jq, jv input, jv a, jv b) {+ jv_free(input);+ if (jv_get_kind(a) == JV_KIND_NULL) {+ jv_free(a);+ return b;+ } else if (jv_get_kind(b) == JV_KIND_NULL) {+ 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_number_value(b));+ } 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) {+ return jv_array_concat(a, b);+ } else if (jv_get_kind(a) == JV_KIND_OBJECT && jv_get_kind(b) == JV_KIND_OBJECT) {+ return jv_object_merge(a, b);+ } else {+ return type_error2(a, b, "cannot be added");+ }+}++#define LIBM_DD(name) \+static jv f_ ## name(jq_state *jq, jv input) { \+ if (jv_get_kind(input) != JV_KIND_NUMBER) { \+ return type_error(input, "number required"); \+ } \+ jv ret = jv_number(name(jv_number_value(input))); \+ jv_free(input); \+ return ret; \+}+#define LIBM_DD_NO(name)++#define LIBM_DDD(name) \+static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \+ if (jv_get_kind(a) != JV_KIND_NUMBER || jv_get_kind(b) != JV_KIND_NUMBER) \+ return type_error(input, "number required"); \+ jv_free(input); \+ jv ret = jv_number(name(jv_number_value(a), jv_number_value(b))); \+ jv_free(a); \+ jv_free(b); \+ return ret; \+}+#define LIBM_DDD_NO(name)+#include "libm.h"+#undef LIBM_DDD_NO+#undef LIBM_DD_NO+#undef LIBM_DDD+#undef LIBM_DD++static jv f_negate(jq_state *jq, jv input) {+ if (jv_get_kind(input) != JV_KIND_NUMBER) {+ return type_error(input, "cannot be negated");+ }+ jv ret = jv_number(-jv_number_value(input));+ jv_free(input);+ 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 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_ltrimstr(jq_state *jq, jv input, jv left) {+ if (jv_get_kind(f_startswith(jq, 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 jv f_rtrimstr(jq_state *jq, jv input, jv right) {+ if (jv_get_kind(f_endswith(jq, 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 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));+ } 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);+ }+ jv_free(a);+ jv_free(b);+ return out;+ } else {+ return type_error2(a, b, "cannot be subtracted");+ }+}++static jv f_multiply(jq_state *jq, jv input, jv a, jv b) {+ jv_kind ak = jv_get_kind(a);+ 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));+ } else if ((ak == JV_KIND_STRING && bk == JV_KIND_NUMBER) ||+ (ak == JV_KIND_NUMBER && bk == JV_KIND_STRING)) {+ jv str = a;+ jv num = b;+ if (ak == JV_KIND_NUMBER) {+ str = b;+ num = a;+ }+ int n;+ size_t alen = jv_string_length_bytes(jv_copy(str));+ jv res = str;++ for (n = jv_number_value(num) - 1; n > 0; n--)+ res = jv_string_append_buf(res, jv_string_value(str), alen);++ jv_free(num);+ if (n < 0) {+ jv_free(str);+ return jv_null();+ }+ return res;+ } else if (ak == JV_KIND_OBJECT && bk == JV_KIND_OBJECT) {+ return jv_object_merge_recursive(a, b);+ } else {+ return type_error2(a, b, "cannot be multiplied");+ }+}++static jv f_divide(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) {+ 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));+ } else if (jv_get_kind(a) == JV_KIND_STRING && jv_get_kind(b) == JV_KIND_STRING) {+ return jv_string_split(a, b);+ } else {+ return type_error2(a, b, "cannot be divided");+ }+}++static jv f_mod(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) {+ 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));+ } else {+ return type_error2(a, b, "cannot be divided (remainder)");+ }+}++static jv f_equal(jq_state *jq, jv input, jv a, jv b) {+ jv_free(input);+ return jv_bool(jv_equal(a, b));+}++static jv f_notequal(jq_state *jq, jv input, jv a, jv b) {+ jv_free(input);+ return jv_bool(!jv_equal(a, b));+}++enum cmp_op {+ CMP_OP_LESS,+ CMP_OP_GREATER,+ CMP_OP_LESSEQ,+ CMP_OP_GREATEREQ+};++static jv order_cmp(jv input, jv a, jv b, enum cmp_op op) {+ jv_free(input);+ int r = jv_cmp(a, b);+ return jv_bool((op == CMP_OP_LESS && r < 0) ||+ (op == CMP_OP_LESSEQ && r <= 0) ||+ (op == CMP_OP_GREATEREQ && r >= 0) ||+ (op == CMP_OP_GREATER && r > 0));+}++static jv f_less(jq_state *jq, jv input, jv a, jv b) {+ return order_cmp(input, a, b, CMP_OP_LESS);+}++static jv f_greater(jq_state *jq, jv input, jv a, jv b) {+ return order_cmp(input, a, b, CMP_OP_GREATER);+}++static jv f_lesseq(jq_state *jq, jv input, jv a, jv b) {+ return order_cmp(input, a, b, CMP_OP_LESSEQ);+}++static jv f_greatereq(jq_state *jq, jv input, jv a, jv b) {+ return order_cmp(input, a, b, CMP_OP_GREATEREQ);+}++static jv f_contains(jq_state *jq, jv a, jv b) {+ if (jv_get_kind(a) == jv_get_kind(b)) {+ return jv_bool(jv_contains(a, b));+ } else {+ return type_error2(a, b, "cannot have their containment checked");+ }+}++static jv f_dump(jq_state *jq, jv input) {+ return jv_dump_string(input, 0);+}++static jv f_json_parse(jq_state *jq, jv input) {+ if (jv_get_kind(input) != JV_KIND_STRING)+ return type_error(input, "only strings can be parsed");+ jv res = jv_parse_sized(jv_string_value(input),+ jv_string_length_bytes(jv_copy(input)));+ jv_free(input);+ return res;+}++static jv f_tonumber(jq_state *jq, jv input) {+ if (jv_get_kind(input) == JV_KIND_NUMBER) {+ return input;+ }+ if (jv_get_kind(input) == JV_KIND_STRING) {+ jv parsed = jv_parse(jv_string_value(input));+ if (!jv_is_valid(parsed) || jv_get_kind(parsed) == JV_KIND_NUMBER) {+ jv_free(input);+ return parsed;+ }+ }+ return type_error(input, "cannot be parsed as a number");+}++static jv f_length(jq_state *jq, jv input) {+ if (jv_get_kind(input) == JV_KIND_ARRAY) {+ return jv_number(jv_array_length(input));+ } else if (jv_get_kind(input) == JV_KIND_OBJECT) {+ return jv_number(jv_object_length(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)));+ } else if (jv_get_kind(input) == JV_KIND_NULL) {+ jv_free(input);+ return jv_number(0);+ } else {+ return type_error(input, "has no length");+ }+}++static jv f_tostring(jq_state *jq, jv input) {+ if (jv_get_kind(input) == JV_KIND_STRING) {+ return input;+ } else {+ return jv_dump_string(input, 0);+ }+}++#define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"++static jv escape_string(jv input, const char* escapings) {++ assert(jv_get_kind(input) == JV_KIND_STRING);+ const char* lookup[128] = {0};+ const char* p = escapings;+ lookup[0] = "\\0";+ while (*p) {+ lookup[(int)*p] = p+1;+ p++;+ p += strlen(p);+ p++;+ }++ jv ret = jv_string("");+ const char* i = jv_string_value(input);+ const char* end = i + jv_string_length_bytes(jv_copy(input));+ const char* cstart;+ int c = 0;+ while ((i = jvp_utf8_next((cstart = i), end, &c))) {+ if (c < 128 && lookup[c]) {+ ret = jv_string_append_str(ret, lookup[c]);+ } else {+ ret = jv_string_append_buf(ret, cstart, i - cstart);+ }+ }+ jv_free(input);+ return ret;++}++static jv f_format(jq_state *jq, jv input, jv fmt) {+ if (jv_get_kind(fmt) != JV_KIND_STRING) {+ jv_free(input);+ return type_error(fmt, "is not a valid format");+ }+ const char* fmt_s = jv_string_value(fmt);+ if (!strcmp(fmt_s, "json")) {+ jv_free(fmt);+ return jv_dump_string(input, 0);+ } else if (!strcmp(fmt_s, "text")) {+ jv_free(fmt);+ return f_tostring(jq, input);+ } else if (!strcmp(fmt_s, "csv") || !strcmp(fmt_s, "tsv")) {+ const char *quotes, *sep, *escapings;+ const char *msg;+ if (!strcmp(fmt_s, "csv")) {+ msg = "cannot be csv-formatted, only array";+ quotes = "\"";+ sep = ",";+ escapings = "\"\"\"\0";+ } else {+ msg = "cannot be tsv-formatted, only array";+ assert(!strcmp(fmt_s, "tsv"));+ quotes = "";+ sep = "\t";+ escapings = "\t\\t\0\r\\r\0\n\\n\0\\\\\\\0";+ }+ jv_free(fmt);+ if (jv_get_kind(input) != JV_KIND_ARRAY)+ return type_error(input, msg);+ jv line = jv_string("");+ jv_array_foreach(input, i, x) {+ if (i) line = jv_string_append_str(line, sep);+ switch (jv_get_kind(x)) {+ case JV_KIND_NULL:+ /* null rendered as empty string */+ jv_free(x);+ break;+ case JV_KIND_TRUE:+ case JV_KIND_FALSE:+ line = jv_string_concat(line, jv_dump_string(x, 0));+ break;+ case JV_KIND_NUMBER:+ if (jv_number_value(x) != jv_number_value(x)) {+ /* NaN, render as empty string */+ jv_free(x);+ } else {+ line = jv_string_concat(line, jv_dump_string(x, 0));+ }+ break;+ case JV_KIND_STRING: {+ line = jv_string_append_str(line, quotes);+ line = jv_string_concat(line, escape_string(x, escapings));+ line = jv_string_append_str(line, quotes);+ break;+ }+ default:+ jv_free(input);+ jv_free(line);+ return type_error(x, "is not valid in a csv row");+ }+ }+ jv_free(input);+ return line;+ } else if (!strcmp(fmt_s, "html")) {+ jv_free(fmt);+ return escape_string(f_tostring(jq, input), "&&\0<<\0>>\0''\0\""\0");+ } else if (!strcmp(fmt_s, "uri")) {+ jv_free(fmt);+ input = f_tostring(jq, input);++ int unreserved[128] = {0};+ const char* p = CHARS_ALPHANUM "-_.!~*'()";+ while (*p) unreserved[(int)*p++] = 1;++ jv line = jv_string("");+ const char* s = jv_string_value(input);+ for (int i=0; i<jv_string_length_bytes(jv_copy(input)); i++) {+ unsigned ch = (unsigned)(unsigned char)*s;+ if (ch < 128 && unreserved[ch]) {+ line = jv_string_append_buf(line, s, 1);+ } else {+ line = jv_string_concat(line, jv_string_fmt("%%%02X", ch));+ }+ s++;+ }+ jv_free(input);+ return line;+ } else if (!strcmp(fmt_s, "sh")) {+ jv_free(fmt);+ if (jv_get_kind(input) != JV_KIND_ARRAY)+ input = jv_array_set(jv_array(), 0, input);+ jv line = jv_string("");+ jv_array_foreach(input, i, x) {+ if (i) line = jv_string_append_str(line, " ");+ switch (jv_get_kind(x)) {+ case JV_KIND_NULL:+ case JV_KIND_TRUE:+ case JV_KIND_FALSE:+ case JV_KIND_NUMBER:+ line = jv_string_concat(line, jv_dump_string(x, 0));+ break;++ case JV_KIND_STRING: {+ line = jv_string_append_str(line, "'");+ line = jv_string_concat(line, escape_string(x, "''\\''\0"));+ line = jv_string_append_str(line, "'");+ break;+ }++ default:+ jv_free(input);+ jv_free(line);+ return type_error(x, "can not be escaped for shell");+ }+ }+ jv_free(input);+ return line;+ } else if (!strcmp(fmt_s, "base64")) {+ jv_free(fmt);+ input = f_tostring(jq, input);+ jv line = jv_string("");+ const char b64[64 + 1] = CHARS_ALPHANUM "+/";+ const unsigned char* data = (const unsigned char*)jv_string_value(input);+ int len = jv_string_length_bytes(jv_copy(input));+ for (int i=0; i<len; i+=3) {+ uint32_t code = 0;+ int n = len - i >= 3 ? 3 : len-i;+ for (int j=0; j<3; j++) {+ code <<= 8;+ code |= j < n ? (unsigned)data[i+j] : 0;+ }+ char buf[4];+ for (int j=0; j<4; j++) {+ buf[j] = b64[(code >> (18 - j*6)) & 0x3f];+ }+ if (n < 3) buf[3] = '=';+ if (n < 2) buf[2] = '=';+ line = jv_string_append_buf(line, buf, sizeof(buf));+ }+ jv_free(input);+ return line;+ } else {+ jv_free(input);+ return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format")));+ }+}++static jv f_keys(jq_state *jq, jv input) {+ if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {+ return jv_keys(input);+ } else {+ return type_error(input, "has no keys");+ }+}++static jv f_keys_unsorted(jq_state *jq, jv input) {+ if (jv_get_kind(input) == JV_KIND_OBJECT || jv_get_kind(input) == JV_KIND_ARRAY) {+ return jv_keys_unsorted(input);+ } else {+ return type_error(input, "has no keys");+ }+}++static jv f_sort(jq_state *jq, jv input){+ if (jv_get_kind(input) == JV_KIND_ARRAY) {+ return jv_sort(input, jv_copy(input));+ } else {+ return type_error(input, "cannot be sorted, as it is not an array");+ }+}++static jv f_sort_by_impl(jq_state *jq, jv input, jv keys) {+ if (jv_get_kind(input) == JV_KIND_ARRAY &&+ jv_get_kind(keys) == JV_KIND_ARRAY &&+ jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {+ return jv_sort(input, keys);+ } else {+ return type_error2(input, keys, "cannot be sorted, as they are not both arrays");+ }+}++static jv f_group_by_impl(jq_state *jq, jv input, jv keys) {+ if (jv_get_kind(input) == JV_KIND_ARRAY &&+ jv_get_kind(keys) == JV_KIND_ARRAY &&+ jv_array_length(jv_copy(input)) == jv_array_length(jv_copy(keys))) {+ return jv_group(input, keys);+ } else {+ return type_error2(input, keys, "cannot be sorted, as they are not both arrays");+ }+}++#ifdef HAVE_ONIGURUMA+static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups,+ int *groups, regex_t *reg, void *arg) {+ jv captures = *(jv*)arg;+ for (int i = 0; i < ngroups; ++i) {+ jv cap = jv_array_get(jv_copy(captures),groups[i]-1);+ if (jv_get_kind(cap) == JV_KIND_OBJECT) {+ cap = jv_object_set(cap, jv_string("name"), jv_string_sized((const char*)name, name_end-name));+ captures = jv_array_set(captures,groups[i]-1,cap);+ } else {+ jv_free(cap);+ }+ }+ *(jv *)arg = captures;+ return 0;+}++static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {+ int test = jv_equal(testmode, jv_true());+ jv result;+ int onigret;+ int global = 0;+ regex_t *reg;+ OnigErrorInfo einfo;+ OnigRegion* region;++ if (jv_get_kind(input) != JV_KIND_STRING) {+ jv_free(regex);+ jv_free(modifiers);+ return type_error(input, "cannot be matched, as it is not a string");+ }++ if (jv_get_kind(regex) != JV_KIND_STRING) {+ jv_free(input);+ jv_free(modifiers);+ return type_error(regex, "is not a string");+ }++ OnigOptionType options = ONIG_OPTION_CAPTURE_GROUP;++ if (jv_get_kind(modifiers) == JV_KIND_STRING) {+ jv modarray = jv_string_explode(jv_copy(modifiers));+ jv_array_foreach(modarray, i, mod) {+ switch ((int)jv_number_value(mod)) {+ case 'g':+ global = 1;+ break;+ case 'i':+ options |= ONIG_OPTION_IGNORECASE;+ break;+ case 'x':+ options |= ONIG_OPTION_EXTEND;+ break;+ case 'm':+ options |= ONIG_OPTION_MULTILINE;+ break;+ case 's':+ options |= ONIG_OPTION_SINGLELINE;+ break;+ case 'p':+ options |= ONIG_OPTION_MULTILINE | ONIG_OPTION_SINGLELINE;+ break;+ case 'l':+ options |= ONIG_OPTION_FIND_LONGEST;+ break;+ case 'n':+ options |= ONIG_OPTION_FIND_NOT_EMPTY;+ break;+ default:+ jv_free(input);+ jv_free(regex);+ jv_free(modarray);+ return jv_invalid_with_msg(jv_string_concat(modifiers,+ jv_string(" is not a valid modifier string")));+ }+ }+ jv_free(modarray);+ } else if (jv_get_kind(modifiers) != JV_KIND_NULL) {+ // If it isn't a string or null, then it is the wrong type...+ jv_free(input);+ jv_free(regex);+ return type_error(modifiers, "is not a string");+ }++ jv_free(modifiers);++ onigret = onig_new(®, (const UChar*)jv_string_value(regex),+ (const UChar*)(jv_string_value(regex) + jv_string_length_bytes(jv_copy(regex))),+ options, ONIG_ENCODING_UTF8, ONIG_SYNTAX_PERL_NG, &einfo);+ if (onigret != ONIG_NORMAL) {+ UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];+ onig_error_code_to_str(ebuf, onigret, &einfo);+ jv_free(input);+ jv_free(regex);+ return jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),+ jv_string((char*)ebuf)));+ }+ result = test ? jv_false() : jv_array();+ const char *input_string = jv_string_value(input);+ const UChar* start = (const UChar*)jv_string_value(input);+ const unsigned long length = jv_string_length_bytes(jv_copy(input));+ const UChar* end = start + length;+ region = onig_region_new();+ do {+ onigret = onig_search(reg,+ (const UChar*)jv_string_value(input), end, /* string boundaries */+ start, end, /* search boundaries */+ region, ONIG_OPTION_NONE);+ if (onigret >= 0) {+ if (test) {+ result = jv_true();+ break;+ }++ // Zero-width match+ if (region->end[0] == region->beg[0]) {+ unsigned long idx;+ const char *fr = (const char*)input_string;+ for (idx = 0; fr != input_string+region->beg[0]; idx++) {+ fr += jvp_utf8_decode_length(*fr);+ }+ jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));+ match = jv_object_set(match, jv_string("length"), jv_number(0));+ match = jv_object_set(match, jv_string("string"), jv_string(""));+ match = jv_object_set(match, jv_string("captures"), jv_array());+ result = jv_array_append(result, match);+ start += 1;+ continue;+ }++ unsigned long idx;+ unsigned long len;+ const char *fr = (const char*)input_string;++ for (idx = len = 0; fr < input_string+region->end[0]; len++) {+ if (fr == input_string+region->beg[0]) idx = len, len=0;+ fr += jvp_utf8_decode_length(*fr);+ }++ jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));++ unsigned long blen = region->end[0]-region->beg[0];+ match = jv_object_set(match, jv_string("length"), jv_number(len));+ match = jv_object_set(match, jv_string("string"), jv_string_sized(input_string+region->beg[0],blen));+ jv captures = jv_array();+ for (int i = 1; i < region->num_regs; ++i) {+ // Empty capture.+ if (region->beg[i] == region->end[i]) {+ // Didn't match.+ jv cap;+ if (region->beg[i] == -1) {+ cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(-1));+ cap = jv_object_set(cap, jv_string("string"), jv_null());+ } else {+ fr = input_string;+ for (idx = 0; fr != input_string+region->beg[i]; idx++) {+ fr += jvp_utf8_decode_length(*fr);+ }+ cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));+ cap = jv_object_set(cap, jv_string("string"), jv_string(""));+ }+ cap = jv_object_set(cap, jv_string("length"), jv_number(0));+ cap = jv_object_set(cap, jv_string("name"), jv_null());+ captures = jv_array_append(captures, cap);+ continue;+ }+ fr = input_string;+ for (idx = len = 0; fr != input_string+region->end[i]; len++) {+ if (fr == input_string+region->beg[i]) idx = len, len=0;+ fr += jvp_utf8_decode_length(*fr);+ }++ blen = region->end[i]-region->beg[i];+ jv cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));+ cap = jv_object_set(cap, jv_string("length"), jv_number(len));+ cap = jv_object_set(cap, jv_string("string"), jv_string_sized(input_string+region->beg[i],blen));+ cap = jv_object_set(cap, jv_string("name"), jv_null());+ captures = jv_array_append(captures,cap);+ }+ onig_foreach_name(reg,f_match_name_iter,&captures);+ match = jv_object_set(match, jv_string("captures"), captures);+ result = jv_array_append(result, match);+ start = (const UChar*)(input_string+region->end[0]);+ onig_region_free(region,0);+ } else if (onigret == ONIG_MISMATCH) {+ break;+ } else { /* Error */+ UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];+ onig_error_code_to_str(ebuf, onigret, einfo);+ jv_free(result);+ result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),+ jv_string((char*)ebuf)));+ break;+ }+ } while (global && start != end);+ onig_region_free(region,1);+ region = NULL;+ if (region)+ onig_region_free(region,1);+ onig_free(reg);+ jv_free(input);+ jv_free(regex);+ return result;+}+#else /* ! HAVE_ONIGURUMA */+static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {+ return jv_invalid_with_msg(jv_string("jq was compiled without ONIGURUMA regex libary. match/test/sub and related functions are not available."));+}+#endif /* HAVE_ONIGURUMA */++static jv minmax_by(jv values, jv keys, int is_min) {+ if (jv_get_kind(values) != JV_KIND_ARRAY)+ return type_error2(values, keys, "cannot be iterated over");+ if (jv_get_kind(keys) != JV_KIND_ARRAY)+ return type_error2(values, keys, "cannot be iterated over");+ if (jv_array_length(jv_copy(values)) != jv_array_length(jv_copy(keys)))+ return type_error2(values, keys, "have wrong length");++ if (jv_array_length(jv_copy(values)) == 0) {+ jv_free(values);+ jv_free(keys);+ return jv_null();+ }+ jv ret = jv_array_get(jv_copy(values), 0);+ jv retkey = jv_array_get(jv_copy(keys), 0);+ for (int i=1; i<jv_array_length(jv_copy(values)); i++) {+ jv item = jv_array_get(jv_copy(keys), i);+ int cmp = jv_cmp(jv_copy(item), jv_copy(retkey));+ if ((cmp < 0) == (is_min == 1)) {+ jv_free(retkey);+ retkey = item;+ jv_free(ret);+ ret = jv_array_get(jv_copy(values), i);+ } else {+ jv_free(item);+ }+ }+ jv_free(values);+ jv_free(keys);+ jv_free(retkey);+ return ret;+}++static jv f_min(jq_state *jq, jv x) {+ return minmax_by(x, jv_copy(x), 1);+}++static jv f_max(jq_state *jq, jv x) {+ return minmax_by(x, jv_copy(x), 0);+}++static jv f_min_by_impl(jq_state *jq, jv x, jv y) {+ return minmax_by(x, y, 1);+}++static jv f_max_by_impl(jq_state *jq, jv x, jv y) {+ return minmax_by(x, y, 0);+}+++static jv f_type(jq_state *jq, jv input) {+ jv out = jv_string(jv_kind_name(jv_get_kind(input)));+ jv_free(input);+ return out;+}++static jv f_isinfinite(jq_state *jq, jv input) {+ jv_kind k = jv_get_kind(input);+ if (k != JV_KIND_NUMBER) {+ jv_free(input);+ return jv_false();+ }+ double n = jv_number_value(input);+ jv_free(input);+ return isinf(n) ? jv_true() : jv_false();+}++static jv f_isnan(jq_state *jq, jv input) {+ jv_kind k = jv_get_kind(input);+ if (k != JV_KIND_NUMBER) {+ jv_free(input);+ return jv_false();+ }+ double n = jv_number_value(input);+ jv_free(input);+ return isnan(n) ? jv_true() : jv_false();+}++static jv f_isnormal(jq_state *jq, jv input) {+ jv_kind k = jv_get_kind(input);+ if (k != JV_KIND_NUMBER) {+ jv_free(input);+ return jv_false();+ }+ double n = jv_number_value(input);+ jv_free(input);+ return isnormal(n) ? jv_true() : jv_false();+}++static jv f_infinite(jq_state *jq, jv input) {+ jv_free(input);+ return jv_number(INFINITY);+}++static jv f_nan(jq_state *jq, jv input) {+ jv_free(input);+ return jv_number(NAN);+}++static jv f_error(jq_state *jq, jv input, jv msg) {+ jv_free(input);+ return jv_invalid_with_msg(msg);+}++// FIXME Should autoconf check for this!+#ifndef WIN32+extern const char **environ;+#endif++static jv f_env(jq_state *jq, jv input) {+ jv_free(input);+ jv env = jv_object();+ const char *var, *val;+ for (const char **e = environ; *e != NULL; e++) {+ var = e[0];+ val = strchr(e[0], '=');+ if (val == NULL)+ env = jv_object_set(env, jv_string(var), jv_null());+ else if (var - val < INT_MAX)+ env = jv_object_set(env, jv_string_sized(var, val - var), jv_string(val + 1));+ }+ return env;+}++static jv f_get_search_list(jq_state *jq, jv input) {+ jv_free(input);+ return jq_get_lib_dirs(jq);+}++static jv f_get_prog_origin(jq_state *jq, jv input) {+ jv_free(input);+ return jq_get_prog_origin(jq);+}++static jv f_get_jq_origin(jq_state *jq, jv input) {+ jv_free(input);+ return jq_get_jq_origin(jq);+}++static jv f_string_split(jq_state *jq, jv a, jv b) {+ if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {+ jv_free(a);+ jv_free(b);+ return jv_invalid_with_msg(jv_string("split input and separator must be strings"));+ }+ return jv_string_split(a, b);+}++static jv f_string_explode(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_STRING) {+ jv_free(a);+ return jv_invalid_with_msg(jv_string("explode input must be a string"));+ }+ return jv_string_explode(a);+}++static jv f_string_indexes(jq_state *jq, jv a, jv b) {+ return jv_string_indexes(a, b);+}++static jv f_string_implode(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_ARRAY) {+ jv_free(a);+ return jv_invalid_with_msg(jv_string("implode input must be an array"));+ }+ return jv_string_implode(a);+}++static jv f_setpath(jq_state *jq, jv a, jv b, jv c) { return jv_setpath(a, b, c); }+static jv f_getpath(jq_state *jq, jv a, jv b) { return jv_getpath(a, b); }+static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); }+static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); }++static jv f_modulemeta(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_STRING) {+ jv_free(a);+ return jv_invalid_with_msg(jv_string("modulemeta input module name must be a string"));+ }+ return load_module_meta(jq, a);+}++static jv f_input(jq_state *jq, jv input) {+ jv_free(input);+ jq_input_cb cb;+ void *data;+ jq_get_input_cb(jq, &cb, &data);+ if (cb == NULL)+ return jv_invalid_with_msg(jv_string("break"));+ jv v = cb(jq, data);+ if (jv_is_valid(v) || jv_invalid_has_msg(jv_copy(v)))+ return v;+ return jv_invalid_with_msg(jv_string("break"));+}++static jv f_debug(jq_state *jq, jv input) {+ jq_msg_cb cb;+ void *data;+ jq_get_debug_cb(jq, &cb, &data);+ if (cb != NULL)+ cb(data, jv_copy(input));+ return input;+}++static jv f_stderr(jq_state *jq, jv input) {+ jv_dumpf(jv_copy(input), stderr, 0);+ fprintf(stderr, "\n");+ return input;+}++static jv tm2jv(struct tm *tm) {+ return JV_ARRAY(jv_number(tm->tm_year + 1900),+ jv_number(tm->tm_mon),+ jv_number(tm->tm_mday),+ jv_number(tm->tm_hour),+ jv_number(tm->tm_min),+ jv_number(tm->tm_sec),+ jv_number(tm->tm_wday),+ jv_number(tm->tm_yday));+}++/*+ * mktime() has side-effects and anyways, returns time in the local+ * timezone, not UTC. We want timegm(), which isn't standard.+ *+ * To make things worse, mktime() tells you what the timezone+ * adjustment is, but you have to #define _BSD_SOURCE to get this+ * field of struct tm on some systems.+ *+ * This is all to blame on POSIX, of course.+ *+ * Our wrapper tries to use timegm() if available, or mktime() and+ * correct for its side-effects if possible.+ *+ * Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.+ */+static time_t my_mktime(struct tm *tm) {+#ifdef HAVE_TIMEGM+ return timegm(tm);+#else /* HAVE_TIMEGM */+ time_t t = mktime(tm);+ if (t == (time_t)-1)+ return t;+#ifdef HAVE_TM_TM_GMT_OFF+ return t + tm.tm_gmtoff;+#elif defined(HAVE_TM_TM_GMT_OFF)+ return t + tm.__tm_gmtoff;+#else+ return (time_t)-2; /* Not supported */+#endif+#endif /* !HAVE_TIMEGM */+}++#ifdef HAVE_STRPTIME+static jv f_strptime(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("strptime/1 requires string inputs and arguments"));++ struct tm tm;+ memset(&tm, 0, sizeof(tm));+ const char *input = jv_string_value(a);+ const char *fmt = jv_string_value(b);+ const char *end = strptime(input, fmt, &tm);++ if (end == NULL || (*end != '\0' && !isspace(*end))) {+ jv e = jv_invalid_with_msg(jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));+ jv_free(a);+ jv_free(b);+ return e;+ }+ jv_free(b);+ if (tm.tm_wday == 0 && tm.tm_yday == 0 && my_mktime(&tm) == (time_t)-2) {+ jv_free(a);+ return jv_invalid_with_msg(jv_string("strptime/1 not supported on this platform"));+ }+ jv r = tm2jv(&tm);+ if (*end != '\0')+ r = jv_array_append(r, jv_string(end));+ jv_free(a); // must come after `*end` because `end` is a pointer into `a`'s string+ return r;+}+#else+static jv f_strptime(jq_state *jq, jv a, jv b) {+ jv_free(a);+ jv_free(b);+ return jv_invalid_with_msg(jv_string("strptime/1 not implemented on this platform"));+}+#endif++#define TO_TM_FIELD(t, j, i) \+ do { \+ jv n = jv_array_get(jv_copy(j), (i)); \+ if (jv_get_kind(n) != (JV_KIND_NUMBER)) \+ return 0; \+ t = jv_number_value(n); \+ jv_free(n); \+ } while (0)++static int jv2tm(jv a, struct tm *tm) {+ memset(tm, 0, sizeof(*tm));+ TO_TM_FIELD(tm->tm_year, a, 0);+ tm->tm_year -= 1900;+ TO_TM_FIELD(tm->tm_mon, a, 1);+ TO_TM_FIELD(tm->tm_mday, a, 2);+ TO_TM_FIELD(tm->tm_hour, a, 3);+ TO_TM_FIELD(tm->tm_min, a, 4);+ TO_TM_FIELD(tm->tm_sec, a, 5);+ TO_TM_FIELD(tm->tm_wday, a, 6);+ TO_TM_FIELD(tm->tm_yday, a, 7);+ jv_free(a);++ // We use UTC everywhere (gettimeofday, gmtime) and UTC does not do DST.+ // Setting tm_isdst to 0 is done by the memset.+ // tm->tm_isdst = 0;++ // The standard permits the tm structure to contain additional members. We+ // hope it is okay to initialize them to zero, because the standard does not+ // provide an alternative.++ return 1;+}++#undef TO_TM_FIELD++static jv f_mktime(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_ARRAY)+ return jv_invalid_with_msg(jv_string("mktime requires array inputs"));+ if (jv_array_length(jv_copy(a)) < 6)+ return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));+ struct tm tm;+ if (!jv2tm(a, &tm))+ return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));+ time_t t = my_mktime(&tm);+ if (t == (time_t)-1)+ return jv_invalid_with_msg(jv_string("invalid gmtime representation"));+ if (t == (time_t)-2)+ return jv_invalid_with_msg(jv_string("mktime not supported on this platform"));+ return jv_number(t);+}++#ifdef HAVE_GMTIME_R+static jv f_gmtime(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_NUMBER)+ return jv_invalid_with_msg(jv_string("gmtime() requires numeric inputs"));+ struct tm tm, *tmp;+ memset(&tm, 0, sizeof(tm));+ double fsecs = jv_number_value(a);+ time_t secs = fsecs;+ jv_free(a);+ tmp = gmtime_r(&secs, &tm);+ if (tmp == NULL)+ return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));+ a = tm2jv(tmp);+ return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));+}+#elif defined HAVE_GMTIME+static jv f_gmtime(jq_state *jq, jv a) {+ if (jv_get_kind(a) != JV_KIND_NUMBER)+ return jv_invalid_with_msg(jv_string("gmtime requires numeric inputs"));+ struct tm tm, *tmp;+ memset(&tm, 0, sizeof(tm));+ double fsecs = jv_number_value(a);+ time_t secs = fsecs;+ jv_free(a);+ tmp = gmtime(&secs);+ if (tmp == NULL)+ return jv_invalid_with_msg(jv_string("errror converting number of seconds since epoch to datetime"));+ a = tm2jv(tmp);+ return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));+}+#else+static jv f_gmtime(jq_state *jq, jv a) {+ jv_free(a);+ return jv_invalid_with_msg(jv_string("gmtime not implemented on this platform"));+}+#endif++#ifdef HAVE_STRFTIME+static jv f_strftime(jq_state *jq, jv a, jv b) {+ if (jv_get_kind(a) == JV_KIND_NUMBER) {+ a = f_gmtime(jq, a);+ } else if (jv_get_kind(a) != JV_KIND_ARRAY) {+ return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs"));+ }+ struct tm tm;+ if (!jv2tm(a, &tm))+ return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs")); \+ const char *fmt = jv_string_value(b);+ size_t alloced = strlen(fmt) + 100;+ char *buf = alloca(alloced);+ size_t n = strftime(buf, alloced, fmt, &tm);+ jv_free(b);+ /* POSIX doesn't provide errno values for strftime() failures; weird */+ if (n == 0 || n > alloced)+ return jv_invalid_with_msg(jv_string("strftime/1: unknown system failure"));+ return jv_string(buf);+}+#else+static jv f_strftime(jq_state *jq, jv a) {+ jv_free(a);+ jv_free(b);+ return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform"));+}+#endif++#ifdef HAVE_GETTIMEOFDAY+static jv f_now(jq_state *jq, jv a) {+ jv_free(a);+ struct timeval tv;+ if (gettimeofday(&tv, NULL) == -1)+ return jv_number(time(NULL));+ return jv_number(tv.tv_sec + tv.tv_usec / 1000000.0);+}+#else+static jv f_now(jq_state *jq, jv a) {+ jv_free(a);+ return jv_number(time(NULL));+}+#endif++static jv f_current_filename(jq_state *jq) {+ jv r = jq_util_input_get_current_filename(jq);+ if (jv_is_valid(r))+ return r;+ jv_free(r);+ return jv_null();+}+static jv f_current_line(jq_state *jq) {+ return jq_util_input_get_current_line(jq);+}++#define LIBM_DD(name) \+ {(cfunction_ptr)f_ ## name, "_" #name, 1},+#define LIBM_DD_NO(name)++#define LIBM_DDD(name) \+ {(cfunction_ptr)f_ ## name, "_" #name, 3},+#define LIBM_DDD_NO(name)++static const struct cfunction function_list[] = {+#include "libm.h"+ {(cfunction_ptr)f_plus, "_plus", 3},+ {(cfunction_ptr)f_negate, "_negate", 1},+ {(cfunction_ptr)f_minus, "_minus", 3},+ {(cfunction_ptr)f_multiply, "_multiply", 3},+ {(cfunction_ptr)f_divide, "_divide", 3},+ {(cfunction_ptr)f_mod, "_mod", 3},+ {(cfunction_ptr)f_dump, "tojson", 1},+ {(cfunction_ptr)f_json_parse, "fromjson", 1},+ {(cfunction_ptr)f_tonumber, "tonumber", 1},+ {(cfunction_ptr)f_tostring, "tostring", 1},+ {(cfunction_ptr)f_keys, "keys", 1},+ {(cfunction_ptr)f_keys_unsorted, "keys_unsorted", 1},+ {(cfunction_ptr)f_startswith, "startswith", 2},+ {(cfunction_ptr)f_endswith, "endswith", 2},+ {(cfunction_ptr)f_ltrimstr, "ltrimstr", 2},+ {(cfunction_ptr)f_rtrimstr, "rtrimstr", 2},+ {(cfunction_ptr)f_string_split, "split", 2},+ {(cfunction_ptr)f_string_explode, "explode", 1},+ {(cfunction_ptr)f_string_implode, "implode", 1},+ {(cfunction_ptr)f_string_indexes, "_strindices", 2},+ {(cfunction_ptr)f_setpath, "setpath", 3}, // FIXME typechecking+ {(cfunction_ptr)f_getpath, "getpath", 2},+ {(cfunction_ptr)f_delpaths, "delpaths", 2},+ {(cfunction_ptr)f_has, "has", 2},+ {(cfunction_ptr)f_equal, "_equal", 3},+ {(cfunction_ptr)f_notequal, "_notequal", 3},+ {(cfunction_ptr)f_less, "_less", 3},+ {(cfunction_ptr)f_greater, "_greater", 3},+ {(cfunction_ptr)f_lesseq, "_lesseq", 3},+ {(cfunction_ptr)f_greatereq, "_greatereq", 3},+ {(cfunction_ptr)f_contains, "contains", 2},+ {(cfunction_ptr)f_length, "length", 1},+ {(cfunction_ptr)f_type, "type", 1},+ {(cfunction_ptr)f_isinfinite, "isinfinite", 1},+ {(cfunction_ptr)f_isnan, "isnan", 1},+ {(cfunction_ptr)f_isnormal, "isnormal", 1},+ {(cfunction_ptr)f_infinite, "infinite", 1},+ {(cfunction_ptr)f_nan, "nan", 1},+ {(cfunction_ptr)f_sort, "sort", 1},+ {(cfunction_ptr)f_sort_by_impl, "_sort_by_impl", 2},+ {(cfunction_ptr)f_group_by_impl, "_group_by_impl", 2},+ {(cfunction_ptr)f_min, "min", 1},+ {(cfunction_ptr)f_max, "max", 1},+ {(cfunction_ptr)f_min_by_impl, "_min_by_impl", 2},+ {(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},+ {(cfunction_ptr)f_error, "error", 2},+ {(cfunction_ptr)f_format, "format", 2},+ {(cfunction_ptr)f_env, "env", 1},+ {(cfunction_ptr)f_get_search_list, "get_search_list", 1},+ {(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},+ {(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},+ {(cfunction_ptr)f_match, "_match_impl", 4},+ {(cfunction_ptr)f_modulemeta, "modulemeta", 1},+ {(cfunction_ptr)f_input, "_input", 1},+ {(cfunction_ptr)f_debug, "debug", 1},+ {(cfunction_ptr)f_stderr, "stderr", 1},+ {(cfunction_ptr)f_strptime, "strptime", 2},+ {(cfunction_ptr)f_strftime, "strftime", 2},+ {(cfunction_ptr)f_mktime, "mktime", 1},+ {(cfunction_ptr)f_gmtime, "gmtime", 1},+ {(cfunction_ptr)f_now, "now", 1},+ {(cfunction_ptr)f_current_filename, "input_filename", 1},+ {(cfunction_ptr)f_current_line, "input_line_number", 1},+};+#undef LIBM_DDD_NO+#undef LIBM_DD_NO+#undef LIBM_DDD+#undef LIBM_DD++struct bytecoded_builtin { const char* name; block code; };+static block bind_bytecoded_builtins(block b) {+ block builtins = gen_noop();+ {+ struct bytecoded_builtin builtin_defs[] = {+ {"empty", gen_op_simple(BACKTRACK)},+ {"not", gen_condbranch(gen_const(jv_false()),+ gen_const(jv_true()))}+ };+ for (unsigned i=0; i<sizeof(builtin_defs)/sizeof(builtin_defs[0]); i++) {+ builtins = BLOCK(builtins, gen_function(builtin_defs[i].name, gen_noop(),+ builtin_defs[i].code));+ }+ }+ {+ struct bytecoded_builtin builtin_def_1arg[] = {+ {"path", BLOCK(gen_op_simple(PATH_BEGIN),+ gen_call("arg", gen_noop()),+ gen_op_simple(PATH_END))},+ };+ for (unsigned i=0; i<sizeof(builtin_def_1arg)/sizeof(builtin_def_1arg[0]); i++) {+ builtins = BLOCK(builtins, gen_function(builtin_def_1arg[i].name,+ gen_param("arg"),+ builtin_def_1arg[i].code));+ }+ }+ {+ // Note that we can now define `range` as a jq-coded function+ block rangevar = gen_op_var_fresh(STOREV, "rangevar");+ block rangestart = gen_op_var_fresh(STOREV, "rangestart");+ block range = BLOCK(gen_op_simple(DUP),+ gen_call("start", gen_noop()),+ rangestart,+ gen_call("end", gen_noop()),+ gen_op_simple(DUP),+ gen_op_bound(LOADV, rangestart),+ // Reset rangevar for every value generated by "end"+ rangevar,+ gen_op_bound(RANGE, rangevar));+ builtins = BLOCK(builtins, gen_function("range",+ BLOCK(gen_param("start"), gen_param("end")),+ range));+ }++ return block_bind_referenced(builtins, b, OP_IS_CALL_PSEUDO);+}++#define LIBM_DD(name) "def " #name ": _" #name ";",+#define LIBM_DDD(name) "def " #name "(a;b): _" #name "(a;b);",+#define LIBM_DD_NO(name) "def " #name ": \"Error: " #name "() not found at build time\"|error;",+#define LIBM_DDD_NO(name) "def " #name "(a;b): \"Error: " #name "() not found at build time\"|error;",++static const char* const jq_builtins[] = {+ "def error: error(.);",+ "def map(f): [.[] | f];",+ "def select(f): if f then . else empty end;",+ "def sort_by(f): _sort_by_impl(map([f]));",+ "def group_by(f): _group_by_impl(map([f]));",+ "def unique: group_by(.) | map(.[0]);",+ "def unique_by(f): group_by(f) | map(.[0]);",+ "def max_by(f): _max_by_impl(map([f]));",+ "def min_by(f): _min_by_impl(map([f]));",+#include "libm.h"+ "def add: reduce .[] as $x (null; . + $x);",+ "def del(f): delpaths([path(f)]);",+ "def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));",+ "def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));",+ "def map_values(f): .[] |= f;",++ // recurse+ "def recurse(f): def r: ., (f | select(. != null) | r); r;",+ "def recurse(f; cond): def r: ., (f | select(cond) | r); r;",+ "def recurse: recurse(.[]?);",+ "def recurse_down: recurse;",++ "def to_entries: [keys_unsorted[] as $k | {key: $k, value: .[$k]}];",+ "def from_entries: map({(.key // .Key // .Name): (if has(\"value\") then .value else .Value end)}) | add | .//={};",+ "def with_entries(f): to_entries | map(f) | from_entries;",+ "def reverse: [.[length - 1 - range(0;length)]];",+ "def indices($i): if type == \"array\" and ($i|type) == \"array\" then .[$i]"+ " elif type == \"array\" then .[[$i]]"+ " elif type == \"string\" and ($i|type) == \"string\" then _strindices($i)"+ " else .[$i] end;",+ "def index($i): indices($i) | .[0];", // TODO: optimize+ "def rindex($i): indices($i) | .[-1:][0];", // TODO: optimize+ "def paths: path(recurse(if (type|. == \"array\" or . == \"object\") then .[] else empty end))|select(length > 0);",+ "def paths(node_filter): . as $dot|paths|select(. as $p|$dot|getpath($p)|node_filter);",+ "def any(generator; condition):"+ " [label $out | foreach generator as $i"+ " (false;"+ " if . then break $out elif $i | condition then true else . end;"+ " if . then . else empty end)] | length == 1;",+ "def any(condition): any(.[]; condition);",+ "def any: any(.);",+ "def all(generator; condition): "+ " [label $out | foreach generator as $i"+ " (true;"+ " if .|not then break $out elif $i | condition then . else false end;"+ " if .|not then . else empty end)] | length == 0;",+ "def all(condition): all(.[]; condition);",+ "def all: all(.);",+ "def isfinite: type == \"number\" and (isinfinite | not);",+ "def arrays: select(type == \"array\");",+ "def objects: select(type == \"object\");",+ "def iterables: arrays, objects;",+ "def booleans: select(type == \"boolean\");",+ "def numbers: select(type == \"number\");",+ "def normals: select(isnormal);",+ "def finites: select(isfinite);",+ "def strings: select(type == \"string\");",+ "def nulls: select(type == \"null\");",+ "def values: select(. != null);",+ "def scalars: select(. == null or . == true or . == false or type == \"number\" or type == \"string\");",+ "def scalars_or_empty: select(. == null or . == true or . == false or type == \"number\" or type == \"string\" or ((type==\"array\" or type==\"object\") and length==0));",+ "def leaf_paths: paths(scalars);",+ "def join($x): reduce .[] as $i (null; (.//\"\") + (if . == null then $i else $x + $i end))//\"\";",+ "def _flatten($x): reduce .[] as $i ([]; if $i | type == \"array\" and $x != 0 then . + ($i | _flatten($x-1)) else . + [$i] end);",+ "def flatten($x): if $x < 0 then error(\"flatten depth must not be negative\") else _flatten($x) end;",+ "def flatten: _flatten(-1);",+ "def range($x): range(0;$x);",+ "def fromdateiso8601: strptime(\"%Y-%m-%dT%H:%M:%SZ\")|mktime;",+ "def todateiso8601: strftime(\"%Y-%m-%dT%H:%M:%SZ\");",+ "def fromdate: fromdateiso8601;",+ "def todate: todateiso8601;",+ "def match(re; mode): _match_impl(re; mode; false)|.[];",+ "def match($val): ($val|type) as $vt | if $vt == \"string\" then match($val; null)"+ " elif $vt == \"array\" and ($val | length) > 1 then match($val[0]; $val[1])"+ " elif $vt == \"array\" and ($val | length) > 0 then match($val[0]; null)"+ " else error( $vt + \" not a string or array\") end;",+ "def test(re; mode): _match_impl(re; mode; true);",+ "def test($val): ($val|type) as $vt | if $vt == \"string\" then test($val; null)"+ " elif $vt == \"array\" and ($val | length) > 1 then test($val[0]; $val[1])"+ " elif $vt == \"array\" and ($val | length) > 0 then test($val[0]; null)"+ " else error( $vt + \" not a string or array\") end;",+ "def capture(re; mods): match(re; mods) | reduce ( .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair ({}; . + $pair);",+ "def capture($val): ($val|type) as $vt | if $vt == \"string\" then capture($val; null)"+ " elif $vt == \"array\" and ($val | length) > 1 then capture($val[0]; $val[1])"+ " elif $vt == \"array\" and ($val | length) > 0 then capture($val[0]; null)"+ " else error( $vt + \" not a string or array\") end;",+ "def scan(re):"+ " match(re; \"g\")"+ " | if (.captures|length > 0)"+ " then [ .captures | .[] | .string ]"+ " else .string"+ " end ;",+ //+ // If input is an array, then emit a stream of successive subarrays of length n (or less),+ // and similarly for strings.+ "def _nwise(a; $n): if a|length <= $n then a else a[0:$n] , _nwise(a[$n:]; $n) end;",+ "def _nwise($n): _nwise(.; $n);",+ //+ // splits/1 produces a stream; split/1 is retained for backward compatibility.+ "def splits($re; flags): . as $s"+ // # multiple occurrences of "g" are acceptable+ " | [ match($re; \"g\" + flags) | (.offset, .offset + .length) ]"+ " | [0] + . +[$s|length]"+ " | _nwise(2)"+ " | $s[.[0]:.[1] ] ;",+ "def splits($re): splits($re; null);",+ //+ // split emits an array for backward compatibility+ "def split($re; flags): [ splits($re; flags) ];",+ //+ // If s contains capture variables, then create a capture object and pipe it to s+ "def sub($re; s):"+ " . as $in"+ " | [match($re)]"+ " | if length == 0 then $in"+ " else .[0]"+ " | . as $r"+ // # create the \"capture\" object:+ " | reduce ( $r | .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair"+ " ({}; . + $pair)"+ " | $in[0:$r.offset] + s + $in[$r.offset+$r.length:]"+ " end ;",+ //+ // If s contains capture variables, then create a capture object and pipe it to s+ "def sub($re; s; flags):"+ " def subg: explode | select(. != 103) | implode;"+ // # "fla" should be flags with all occurrences of g removed; gs should be non-nil if flags has a g+ " def sub1(fla; gs):"+ " def mysub:"+ " . as $in"+ " | [match($re; fla)]"+ " | if length == 0 then $in"+ " else .[0] as $edit"+ " | ($edit | .offset + .length) as $len"+ // # create the "capture" object:+ " | reduce ( $edit | .captures | .[] | select(.name != null) | { (.name) : .string } ) as $pair"+ " ({}; . + $pair)"+ " | $in[0:$edit.offset]"+ " + s"+ " + ($in[$len:] | if gs then mysub else . end)"+ " end ;"+ " mysub ;"+ " (flags | index(\"g\")) as $gs"+ " | (flags | if $gs then subg else . end) as $fla"+ " | sub1($fla; $gs);",+ //+ "def sub($re; s): sub($re; s; \"\");",+ // repeated substitution of re (which may contain named captures)+ "def gsub($re; s; flags): sub($re; s; flags + \"g\");",+ "def gsub($re; s): sub($re; s; \"g\");",++ //#######################################################################+ // range/3, with a `by` expression argument+ "def range($init; $upto; $by): "+ " def _range: "+ " if ($by > 0 and . < $upto) or ($by < 0 and . > $upto) then ., ((.+$by)|_range) else . end; "+ " if $by == 0 then $init else $init|_range end | select(($by > 0 and . < $upto) or ($by < 0 and . > $upto));",+ // generic iterator/generator+ "def while(cond; update): "+ " def _while: "+ " if cond then ., (update | _while) else empty end; "+ " _while;",+ "def until(cond; next): "+ " def _until: "+ " if cond then . else (next|_until) end;"+ " _until;",+ "def limit($n; exp): if $n < 0 then exp else label $out | foreach exp as $item ([$n, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1]) end;",+ "def first(g): label $out | foreach g as $item ([false, null]; if .[0]==true then break $out else [true, $item] end; .[1]);",+ "def last(g): reduce g as $item (null; $item);",+ "def nth($n; g): if $n < 0 then error(\"nth doesn't support negative indices\") else last(limit($n + 1; g)) end;",+ "def first: .[0];",+ "def last: .[-1];",+ "def nth($n): .[$n];",+ "def combinations:"+ " if length == 0 then [] else"+ " .[0][] as $x"+ " | (.[1:] | combinations) as $y"+ " | [$x] + $y"+ " end;",+ "def combinations(n):"+ " . as $dot"+ " | [range(n) | $dot]"+ " | combinations;",+ // # transpose a possibly jagged matrix, quickly;+ // # rows are padded with nulls so the result is always rectangular.+ "def transpose:"+ " if . == [] then []"+ " else . as $in"+ " | (map(length) | max) as $max"+ " | length as $length"+ " | reduce range(0; $max) as $j"+ " ([]; . + [reduce range(0;$length) as $i ([]; . + [ $in[$i][$j] ] )] )"+ " end;",+ "def in(xs): . as $x | xs | has($x);",+ "def inside(xs): . as $x | xs | contains($x);",+ "def input: _input;",+ "def repeat(exp): "+ " def _repeat: "+ " exp, _repeat;"+ " _repeat;",+ "def inputs: try repeat(_input) catch if .==\"break\" then empty else .|error end;",+ // # like ruby's downcase - only characters A to Z are affected+ "def ascii_downcase:"+ " explode | map( if 65 <= . and . <= 90 then . + 32 else . end) | implode;",+ // # like ruby's upcase - only characters a to z are affected+ "def ascii_upcase:"+ " explode | map( if 97 <= . and . <= 122 then . - 32 else . end) | implode;",++ // Streaming utilities+ "def truncate_stream(stream):"+ " . as $n | null | stream | . as $input | if (.[0]|length) > $n then setpath([0];$input[0][1:]) else empty end;",+ "def fromstream(i):"+ " foreach i as $item ("+ " [null,false,null,false];"+ " if ($item[0]|length) == 0 then [null,false,.[2],.[3]]"+ " elif ($item|length) == 1 and ($item[0]|length) < 2 then [null,false,.[0],.[1]]"+ " else . end |"+ " . as $state |"+ " if ($item|length) > 1 and ($item[0]|length) > 0 then"+ " [.[0]|setpath(($item|.[0]); ($item|.[1])), "+ " true, "+ " $state[2], "+ " $state[3]] "+ " else ."+ " end;"+ " if ($item[0]|length) == 1 and ($item|length == 1) and .[3] then .[2] else empty end,"+ " if ($item[0]|length) == 0 then $item[1] else empty end"+ " );",+ "def tostream:\n"+ " {string:true,number:true,boolean:true,null:true} as $leaf_types |\n"+ " . as $dot |\n"+ " if $leaf_types[$dot|type] or length==0 then [[],$dot]\n"+ " else\n"+ " # We really need a _streaming_ form of `keys`.\n"+ " # We can use `range` for arrays, but not for objects.\n"+ " keys as $keys |\n"+ " $keys[-1] as $last|\n"+ " ((# for each key\n"+ " $keys[] | . as $key |\n"+ " $dot[$key] | . as $dot |\n"+ " # recurse on each key/value\n"+ " tostream|.[0]|=[$key]+.),\n"+ " # then add the closing marker\n"+ " [[$last]])\n"+ " end;",+++ // # Assuming the input array is sorted, bsearch/1 returns+ // # the index of the target if the target is in the input array; and otherwise+ // # (-1 - ix), where ix is the insertion point that would leave the array sorted.+ // # If the input is not sorted, bsearch will terminate but with irrelevant results.+ "def bsearch(target):"+ " if length == 0 then -1"+ " elif length == 1 then"+ " if target == .[0] then 0 elif target < .[0] then -1 else -2 end"+ " else . as $in"+ "" // # state variable: [start, end, answer]+ "" // # where start and end are the upper and lower offsets to use.+ " | [0, length-1, null]"+ " | until( .[0] > .[1] ;"+ " if .[2] != null then (.[1] = -1)" // # i.e. break+ " else"+ " ( ( (.[1] + .[0]) / 2 ) | floor ) as $mid"+ " | $in[$mid] as $monkey"+ " | if $monkey == target then (.[2] = $mid)" // # success+ " elif .[0] == .[1] then (.[1] = -1)" // # failure+ " elif $monkey < target then (.[0] = ($mid + 1))"+ " else (.[1] = ($mid - 1))"+ " end"+ " end )"+ " | if .[2] == null then" // # compute the insertion point+ " if $in[ .[0] ] < target then (-2 -.[0])"+ " else (-1 -.[0])"+ " end"+ " else .[2]"+ " end"+ " end;",++ // # Apply f to composite entities recursively, and to atoms+ "def walk(f):"+ " . as $in"+ " | if type == \"object\" then"+ " reduce keys[] as $key"+ " ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f"+ " elif type == \"array\" then map( walk(f) ) | f"+ " else f"+ " end;",+};++#undef LIBM_DDD_NO+#undef LIBM_DD_NO+#undef LIBM_DDD+#undef LIBM_DD+++static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {+ struct locfile* src;+ src = locfile_init(jq, "<builtin>", code, strlen(code));+ block funcs;+ int nerrors = jq_parse_library(src, &funcs);+ if (nerrors == 0) {+ *bb = block_bind_referenced(funcs, *bb, OP_IS_CALL_PSEUDO);+ }+ locfile_free(src);+ return nerrors;+}++static int slurp_lib(jq_state *jq, block* bb) {+ int nerrors = 0;+ char* home = getenv("HOME");+ if (home) { // silently ignore no $HOME+ jv filename = jv_string_append_str(jv_string(home), "/.jq");+ jv data = jv_load_file(jv_string_value(filename), 1);+ if (jv_is_valid(data)) {+ nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );+ }+ jv_free(filename);+ jv_free(data);+ }+ return nerrors;+}++int builtins_bind(jq_state *jq, block* bb) {+ int nerrors = slurp_lib(jq, bb);+ if (nerrors) {+ block_free(*bb);+ return nerrors;+ }+ for (int i=(int)(sizeof(jq_builtins)/sizeof(jq_builtins[0]))-1; i>=0; i--) {+ nerrors = builtins_bind_one(jq, bb, jq_builtins[i]);+ assert(!nerrors);+ }+ *bb = bind_bytecoded_builtins(*bb);+ *bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);+ return nerrors;+}
@@ -0,0 +1,10 @@+#ifndef BUILTIN_H+#define BUILTIN_H++#include "jq.h"+#include "bytecode.h"+#include "compile.h"++int builtins_bind(jq_state *, block*);++#endif
@@ -0,0 +1,161 @@+#include <stdio.h>+#include <stdint.h>+#include <stdlib.h>++#include "bytecode.h"+#include "jv_alloc.h"++// flags, length+#define NONE 0, 1+#define CONSTANT OP_HAS_CONSTANT, 2+#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3+#define GLOBAL (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4+#define BRANCH OP_HAS_BRANCH, 2+#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3+#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4+#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0+#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2++#define OP(name, imm, in, out) \+ {name, #name, imm, in, out},++static const struct opcode_description opcode_descriptions[] = {+#include "opcode_list.h"+};++static const struct opcode_description invalid_opcode_description = {+ -1, "#INVALID", 0, 0, 0, 0+};+++const struct opcode_description* opcode_describe(opcode op) {+ if ((int)op >= 0 && (int)op < NUM_OPCODES) {+ return &opcode_descriptions[op];+ } else {+ return &invalid_opcode_description;+ }+}+++int bytecode_operation_length(uint16_t* codeptr) {+ int length = opcode_describe(*codeptr)->length;+ if (*codeptr == CALL_JQ || *codeptr == TAIL_CALL_JQ) {+ length += codeptr[1] * 2;+ }+ return length;+}++static void dump_code(int indent, struct bytecode* bc) {+ int pc = 0;+ while (pc < bc->codelen) {+ printf("%*s", indent, "");+ dump_operation(bc, bc->code + pc);+ printf("\n");+ pc += bytecode_operation_length(bc->code + pc);+ }+}++static void symbol_table_free(struct symbol_table* syms) {+ jv_mem_free(syms->cfunctions);+ jv_free(syms->cfunc_names);+ jv_mem_free(syms);+}++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 struct bytecode* getlevel(struct bytecode* bc, int level) {+ while (level > 0) {+ bc = bc->parent;+ level--;+ }+ return bc;+}++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);+ }+ }+}++void bytecode_free(struct bytecode* bc) {+ if (!bc)+ return;+ jv_mem_free(bc->code);+ jv_free(bc->constants);+ for (int i=0; i<bc->nsubfunctions; i++)+ bytecode_free(bc->subfunctions[i]);+ if (!bc->parent)+ symbol_table_free(bc->globals);+ jv_mem_free(bc->subfunctions);+ jv_free(bc->debuginfo);+ jv_mem_free(bc);+}
@@ -0,0 +1,92 @@+#ifndef BYTECODE_H+#define BYTECODE_H+#include <stdint.h>++#include "jv.h"++typedef enum {+#define OP(name, imm, in, out) name,+#include "opcode_list.h"+#undef OP+} opcode;++enum {+ NUM_OPCODES =+#define OP(name, imm, in, out) +1+#include "opcode_list.h"+#undef OP+};++enum {+ OP_HAS_CONSTANT = 2,+ OP_HAS_VARIABLE = 4,+ OP_HAS_BRANCH = 8,+ OP_HAS_CFUNC = 32,+ OP_HAS_UFUNC = 64,+ OP_IS_CALL_PSEUDO = 128,+ OP_HAS_BINDING = 1024,+ // NOTE: Not actually part of any op -- a pseudo-op flag for special+ // handling of `break`.+ OP_BIND_WILDCARD = 2048,+};+struct opcode_description {+ opcode op;+ const char* name;++ int flags;++ // length in 16-bit units+ int length;++ int stack_in, stack_out;+};++const struct opcode_description* opcode_describe(opcode op);+++#define MAX_CFUNCTION_ARGS 10+typedef void (*cfunction_ptr)();+struct cfunction {+ cfunction_ptr fptr;+ const char* name;+ int nargs;+};++struct symbol_table {+ struct cfunction* cfunctions;+ int ncfunctions;+ jv cfunc_names;+};++// The bytecode format matters in:+// execute.c - interpreter+// compile.c - compiler+// bytecode.c - disassembler++#define ARG_NEWCLOSURE 0x1000++struct bytecode {+ uint16_t* code;+ int codelen;++ int nlocals;+ int nclosures;++ jv constants; // JSON array of constants+ struct symbol_table* globals;++ struct bytecode** subfunctions;+ int nsubfunctions;++ struct bytecode* parent;++ jv debuginfo;+};++void dump_disassembly(int, struct bytecode* code);+void dump_operation(struct bytecode* bc, uint16_t* op);++int bytecode_operation_length(uint16_t* codeptr);+void bytecode_free(struct bytecode* bc);++#endif
@@ -0,0 +1,1213 @@+#ifndef _GNU_SOURCE+#define _GNU_SOURCE // for strdup+#endif+#include <assert.h>+#include <math.h>+#include <string.h>+#include <stdlib.h>+#include "compile.h"+#include "bytecode.h"+#include "locfile.h"+#include "jv_alloc.h"+#include "linker.h"++/*+ The intermediate representation for jq filters is as a sequence of+ struct inst, which form a doubly-linked list via the next and prev+ pointers.++ A "block" represents a sequence of "struct inst", which may be+ empty.++ Blocks are generated by the parser bottom-up, so may have free+ variables (refer to things not defined). See inst.bound_by and+ inst.symbol.+ */+struct inst {+ struct inst* next;+ struct inst* prev;++ opcode op;++ struct {+ uint16_t intval;+ struct inst* target;+ jv constant;+ const struct cfunction* cfunc;+ } imm;++ struct locfile* locfile;+ location source;++ // Binding+ // An instruction requiring binding (for parameters/variables/functions)+ // is in one of three states:+ // inst->bound_by = NULL - Unbound free variable+ // inst->bound_by = inst - This instruction binds a variable+ // inst->bound_by = other - Uses variable bound by other instruction+ // Unbound instructions (references to other things that may or may not+ // exist) are created by "gen_foo_unbound", and bindings are created by+ // block_bind(definition, body), which binds all instructions in+ // body which are unboudn and refer to "definition" by name.+ struct inst* bound_by;+ char* symbol;++ int nformals;+ int nactuals;++ block subfn; // used by CLOSURE_CREATE (body of function)+ block arglist; // used by CLOSURE_CREATE (formals) and CALL_JQ (arguments)++ // This instruction is compiled as part of which function?+ // (only used during block_compile)+ struct bytecode* compiled;++ int bytecode_pos; // position just after this insn+};++static inst* inst_new(opcode op) {+ inst* i = jv_mem_alloc(sizeof(inst));+ i->next = i->prev = 0;+ i->op = op;+ i->bytecode_pos = -1;+ i->bound_by = 0;+ i->symbol = 0;+ i->nformals = -1;+ i->nactuals = -1;+ i->subfn = gen_noop();+ i->arglist = gen_noop();+ i->source = UNKNOWN_LOCATION;+ i->locfile = 0;+ return i;+}++static void inst_free(struct inst* i) {+ jv_mem_free(i->symbol);+ block_free(i->subfn);+ block_free(i->arglist);+ if (i->locfile)+ locfile_free(i->locfile);+ if (opcode_describe(i->op)->flags & OP_HAS_CONSTANT) {+ jv_free(i->imm.constant);+ }+ jv_mem_free(i);+}++static block inst_block(inst* i) {+ block b = {i,i};+ return b;+}++int block_is_single(block b) {+ return b.first && b.first == b.last;+}++static inst* block_take(block* b) {+ if (b->first == 0) return 0;+ inst* i = b->first;+ if (i->next) {+ i->next->prev = 0;+ b->first = i->next;+ i->next = 0;+ } else {+ b->first = 0;+ b->last = 0;+ }+ return i;+}++block gen_location(location loc, struct locfile* l, block b) {+ for (inst* i = b.first; i; i = i->next) {+ if (i->source.start == UNKNOWN_LOCATION.start &&+ i->source.end == UNKNOWN_LOCATION.end) {+ i->source = loc;+ i->locfile = locfile_retain(l);+ }+ }+ return b;+}++block gen_noop() {+ block b = {0,0};+ return b;+}++int block_is_noop(block b) {+ return (b.first == 0 && b.last == 0);+}++block gen_op_simple(opcode op) {+ assert(opcode_describe(op)->length == 1);+ return inst_block(inst_new(op));+}+++block gen_const(jv constant) {+ assert(opcode_describe(LOADK)->flags & OP_HAS_CONSTANT);+ inst* i = inst_new(LOADK);+ i->imm.constant = constant;+ return inst_block(i);+}++block gen_const_global(jv constant, const char *name) {+ assert((opcode_describe(STORE_GLOBAL)->flags & (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING)) ==+ (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING));+ inst* i = inst_new(STORE_GLOBAL);+ i->imm.constant = constant;+ i->symbol = strdup(name);+ return inst_block(i);+}++int block_is_const(block b) {+ return (block_is_single(b) && b.first->op == LOADK);+}++int block_is_const_inf(block b) {+ return (block_is_single(b) && b.first->op == LOADK &&+ jv_get_kind(b.first->imm.constant) == JV_KIND_NUMBER &&+ isinf(jv_number_value(b.first->imm.constant)));+}++jv_kind block_const_kind(block b) {+ assert(block_is_const(b));+ return jv_get_kind(b.first->imm.constant);+}++jv block_const(block b) {+ assert(block_is_const(b));+ return jv_copy(b.first->imm.constant);+}++block gen_op_target(opcode op, block target) {+ assert(opcode_describe(op)->flags & OP_HAS_BRANCH);+ assert(target.last);+ inst* i = inst_new(op);+ i->imm.target = target.last;+ return inst_block(i);+}++block gen_op_targetlater(opcode op) {+ assert(opcode_describe(op)->flags & OP_HAS_BRANCH);+ inst* i = inst_new(op);+ i->imm.target = 0;+ return inst_block(i);+}+void inst_set_target(block b, block target) {+ assert(block_is_single(b));+ assert(opcode_describe(b.first->op)->flags & OP_HAS_BRANCH);+ assert(target.last);+ b.first->imm.target = target.last;+}++block gen_op_unbound(opcode op, const char* name) {+ assert(opcode_describe(op)->flags & OP_HAS_BINDING);+ inst* i = inst_new(op);+ i->symbol = strdup(name);+ return inst_block(i);+}++block gen_op_var_fresh(opcode op, const char* name) {+ assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);+ return block_bind(gen_op_unbound(op, name),+ gen_noop(), OP_HAS_VARIABLE);+}++block gen_op_bound(opcode op, block binder) {+ assert(block_is_single(binder));+ block b = gen_op_unbound(op, binder.first->symbol);+ b.first->bound_by = binder.first;+ return b;+}+++static void inst_join(inst* a, inst* b) {+ assert(a && b);+ assert(!a->next);+ assert(!b->prev);+ a->next = b;+ b->prev = a;+}++void block_append(block* b, block b2) {+ if (b2.first) {+ if (b->last) {+ inst_join(b->last, b2.first);+ } else {+ b->first = b2.first;+ }+ b->last = b2.last;+ }+}++block block_join(block a, block b) {+ block c = a;+ block_append(&c, b);+ return c;+}++int block_has_only_binders_and_imports(block binders, int bindflags) {+ bindflags |= OP_HAS_BINDING;+ for (inst* curr = binders.first; curr; curr = curr->next) {+ if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != DEPS && curr->op != MODULEMETA) {+ return 0;+ }+ }+ return 1;+}++static int inst_is_binder(inst *i, int bindflags) {+ return !((opcode_describe(i->op)->flags & bindflags) != bindflags && i->op != MODULEMETA);+}++int block_has_only_binders(block binders, int bindflags) {+ bindflags |= OP_HAS_BINDING;+ bindflags &= ~OP_BIND_WILDCARD;+ for (inst* curr = binders.first; curr; curr = curr->next) {+ if ((opcode_describe(curr->op)->flags & bindflags) != bindflags && curr->op != MODULEMETA) {+ return 0;+ }+ }+ return 1;+}++// Count a binder's (function) formal params+static int block_count_formals(block b) {+ int args = 0;+ if (b.first->op == CLOSURE_CREATE_C)+ return b.first->imm.cfunc->nargs - 1;+ for (inst* i = b.first->arglist.first; i; i = i->next) {+ assert(i->op == CLOSURE_PARAM);+ args++;+ }+ return args;+}++// Count a call site's actual params+static int block_count_actuals(block b) {+ int args = 0;+ for (inst* i = b.first; i; i = i->next) {+ switch (i->op) {+ default: assert(0 && "Unknown function type"); break;+ case CLOSURE_CREATE:+ case CLOSURE_PARAM:+ case CLOSURE_CREATE_C:+ args++;+ break;+ }+ }+ return args;+}++static int block_count_refs(block binder, block body) {+ int nrefs = 0;+ for (inst* i = body.first; i; i = i->next) {+ if (i != binder.first && i->bound_by == binder.first) {+ nrefs++;+ }+ // counting recurses into closures+ nrefs += block_count_refs(binder, i->subfn);+ // counting recurses into argument list+ nrefs += block_count_refs(binder, i->arglist);+ }+ return nrefs;+}++static int block_bind_subblock(block binder, block body, int bindflags, int break_distance) {+ assert(block_is_single(binder));+ assert((opcode_describe(binder.first->op)->flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD));+ assert(binder.first->symbol);+ assert(binder.first->bound_by == 0 || binder.first->bound_by == binder.first);+ assert(break_distance >= 0);++ binder.first->bound_by = binder.first;+ if (binder.first->nformals == -1)+ binder.first->nformals = block_count_formals(binder);+ int nrefs = 0;+ for (inst* i = body.first; i; i = i->next) {+ int flags = opcode_describe(i->op)->flags;+ if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by == 0 &&+ (!strcmp(i->symbol, binder.first->symbol) ||+ // Check for break/break2/break3; see parser.y+ ((bindflags & OP_BIND_WILDCARD) && i->symbol[0] == '*' &&+ break_distance <= 3 && (i->symbol[1] == '1' + break_distance) &&+ i->symbol[2] == '\0'))) {+ // bind this instruction+ if (i->op == CALL_JQ && i->nactuals == -1)+ i->nactuals = block_count_actuals(i->arglist);+ if (i->nactuals == -1 || i->nactuals == binder.first->nformals) {+ i->bound_by = binder.first;+ nrefs++;+ }+ } else if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by != 0 &&+ !strncmp(binder.first->symbol, "*anonlabel", sizeof("*anonlabel") - 1) &&+ !strncmp(i->symbol, "*anonlabel", sizeof("*anonlabel") - 1)) {+ // Increment the break distance required for this binder to match+ // a break whenever we come across a STOREV of *anonlabel...+ break_distance++;+ }+ // binding recurses into closures+ nrefs += block_bind_subblock(binder, i->subfn, bindflags, break_distance);+ // binding recurses into argument list+ nrefs += block_bind_subblock(binder, i->arglist, bindflags, break_distance);+ }+ return nrefs;+}++static int block_bind_each(block binder, block body, int bindflags) {+ assert(block_has_only_binders(binder, bindflags));+ bindflags |= OP_HAS_BINDING;+ int nrefs = 0;+ for (inst* curr = binder.first; curr; curr = curr->next) {+ nrefs += block_bind_subblock(inst_block(curr), body, bindflags, 0);+ }+ return nrefs;+}++block block_bind(block binder, block body, int bindflags) {+ block_bind_each(binder, body, bindflags);+ return block_join(binder, body);+}++block block_bind_library(block binder, block body, int bindflags, const char *libname) {+ bindflags |= OP_HAS_BINDING;+ int nrefs = 0;+ int matchlen = (libname == NULL) ? 0 : strlen(libname);+ char *matchname = jv_mem_alloc(matchlen+2+1);+ matchname[0] = '\0';+ if (libname != NULL && libname[0] != '\0') {+ strcpy(matchname,libname);+ strcpy(matchname+matchlen, "::");+ matchlen += 2;+ }+ assert(block_has_only_binders(binder, bindflags));+ for (inst *curr = binder.first; curr; curr = curr->next) {+ int bindflags2 = bindflags;+ char* cname = curr->symbol;+ char* tname = jv_mem_alloc(strlen(curr->symbol)+matchlen+1);+ strcpy(tname, matchname);+ strcpy(tname+matchlen, curr->symbol);++ // Ew+ if ((opcode_describe(curr->op)->flags & (OP_HAS_VARIABLE | OP_HAS_CONSTANT)))+ bindflags2 = OP_HAS_VARIABLE | OP_HAS_BINDING;++ // This mutation is ugly, even if we undo it+ curr->symbol = tname;+ nrefs += block_bind_subblock(inst_block(curr), body, bindflags2, 0);+ curr->symbol = cname;+ free(tname);+ }+ free(matchname);+ return body; // We don't return a join because we don't want those sticking around...+}++// Bind binder to body and throw away any defs in binder not referenced+// (directly or indirectly) from body.+block block_bind_referenced(block binder, block body, int bindflags) {+ assert(block_has_only_binders(binder, bindflags));+ bindflags |= OP_HAS_BINDING;+ block refd = gen_noop();+ block unrefd = gen_noop();+ int nrefs;+ for (int last_kept = 0, kept = 0; ; ) {+ for (inst* curr; (curr = block_take(&binder));) {+ block b = inst_block(curr);+ nrefs = block_bind_each(b, body, bindflags);+ // Check if this binder is referenced from any of the ones we+ // already know are referenced by body.+ nrefs += block_count_refs(b, refd);+ nrefs += block_count_refs(b, body);+ if (nrefs) {+ refd = BLOCK(refd, b);+ kept++;+ } else {+ unrefd = BLOCK(unrefd, b);+ }+ }+ if (kept == last_kept)+ break;+ last_kept = kept;+ binder = unrefd;+ unrefd = gen_noop();+ }+ block_free(unrefd);+ return block_join(refd, body);+}++block block_drop_unreferenced(block body) {+ inst* curr;+ block refd = gen_noop();+ block unrefd = gen_noop();+ int drop;+ do {+ drop = 0;+ while ((curr = block_take(&body)) && curr->op != TOP) {+ block b = inst_block(curr);+ if (block_count_refs(b,refd) + block_count_refs(b,body) == 0) {+ unrefd = BLOCK(unrefd, b);+ drop++;+ } else {+ refd = BLOCK(refd, b);+ }+ }+ if (curr && curr->op == TOP) {+ body = BLOCK(inst_block(curr),body);+ }+ body = BLOCK(refd, body);+ refd = gen_noop();+ } while (drop != 0);+ block_free(unrefd);+ return body;+}++jv block_take_imports(block* body) {+ jv imports = jv_array();++ inst* top = NULL;+ if (body->first && body->first->op == TOP) {+ top = block_take(body);+ }+ while (body->first && (body->first->op == MODULEMETA || body->first->op == DEPS)) {+ inst* dep = block_take(body);+ if (dep->op == DEPS) {+ imports = jv_array_append(imports, jv_copy(dep->imm.constant));+ }+ inst_free(dep);+ }+ if (top) {+ *body = block_join(inst_block(top),*body);+ }+ return imports;+}++block gen_module(block metadata) {+ inst* i = inst_new(MODULEMETA);+ i->imm.constant = block_const(metadata);+ if (jv_get_kind(i->imm.constant) != JV_KIND_OBJECT)+ i->imm.constant = jv_object_set(jv_object(), jv_string("metadata"), i->imm.constant);+ block_free(metadata);+ return inst_block(i);+}++jv block_module_meta(block b) {+ if (b.first != NULL && b.first->op == MODULEMETA)+ return jv_copy(b.first->imm.constant);+ return jv_null();+}++block gen_import(const char* name, const char* as, int is_data) {+ inst* i = inst_new(DEPS);+ jv meta = jv_object();+ if (as != NULL)+ meta = jv_object_set(meta, jv_string("as"), jv_string(as));+ meta = jv_object_set(meta, jv_string("is_data"), is_data ? jv_true() : jv_false());+ meta = jv_object_set(meta, jv_string("relpath"), jv_string(name));+ i->imm.constant = meta;+ return inst_block(i);+}++block gen_import_meta(block import, block metadata) {+ assert(block_is_single(import) && import.first->op == DEPS);+ assert(block_is_const(metadata) && block_const_kind(metadata) == JV_KIND_OBJECT);+ inst *i = import.first;+ i->imm.constant = jv_object_merge(block_const(metadata), i->imm.constant);+ block_free(metadata);+ return import;+}++block gen_function(const char* name, block formals, block body) {+ inst* i = inst_new(CLOSURE_CREATE);+ for (inst* i = formals.last; i; i = i->prev) {+ if (i->op == CLOSURE_PARAM_REGULAR) {+ i->op = CLOSURE_PARAM;+ body = gen_var_binding(gen_call(i->symbol, gen_noop()), i->symbol, body);+ }+ block_bind_subblock(inst_block(i), body, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);+ }+ i->subfn = body;+ i->symbol = strdup(name);+ i->arglist = formals;+ block b = inst_block(i);+ block_bind_subblock(b, b, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);+ return b;+}++block gen_param_regular(const char* name) {+ return gen_op_unbound(CLOSURE_PARAM_REGULAR, name);+}++block gen_param(const char* name) {+ return gen_op_unbound(CLOSURE_PARAM, name);+}++block gen_lambda(block body) {+ return gen_function("@lambda", gen_noop(), body);+}++block gen_call(const char* name, block args) {+ block b = gen_op_unbound(CALL_JQ, name);+ b.first->arglist = args;+ return b;+}++++block gen_subexp(block a) {+ return BLOCK(gen_op_simple(SUBEXP_BEGIN), a, gen_op_simple(SUBEXP_END));+}++block gen_both(block a, block b) {+ block jump = gen_op_targetlater(JUMP);+ block fork = gen_op_target(FORK, jump);+ block c = BLOCK(fork, a, jump, b);+ inst_set_target(jump, c);+ return c;+}++block gen_const_object(block expr) {+ int is_const = 1;+ jv o = jv_object();+ jv k = jv_null();+ jv v = jv_null();+ for (inst *i = expr.first; i; i = i->next) {+ if (i->op != SUBEXP_BEGIN ||+ i->next == NULL ||+ i->next->op != LOADK ||+ i->next->next == NULL ||+ i->next->next->op != SUBEXP_END) {+ is_const = 0;+ break;+ }+ k = jv_copy(i->next->imm.constant);+ i = i->next->next->next;+ if (i == NULL ||+ i->op != SUBEXP_BEGIN ||+ i->next == NULL ||+ i->next->op != LOADK ||+ i->next->next == NULL ||+ i->next->next->op != SUBEXP_END) {+ is_const = 0;+ break;+ }+ v = jv_copy(i->next->imm.constant);+ i = i->next->next->next;+ if (i == NULL || i->op != INSERT) {+ is_const = 0;+ break;+ }+ o = jv_object_set(o, k, v);+ k = jv_null();+ v = jv_null();+ }+ if (!is_const) {+ jv_free(o);+ jv_free(k);+ jv_free(v);+ block b = {0,0};+ return b;+ }+ block_free(expr);+ return gen_const(o);+}++static block gen_const_array(block expr) {+ /*+ * An expr of all constant elements looks like this:+ *+ * 0009 FORK 0027+ * 0011 FORK 0023+ * 0013 FORK 0019+ * 0015 LOADK 1+ * 0017 JUMP 0021+ * 0019 LOADK 2+ * 0021 JUMP 0025+ * 0023 LOADK 3+ * 0025 JUMP 0029+ * 0027 LOADK 4+ *+ * That's: N-1 commas for N elements, N LOADKs, and a JUMP between+ * every LOADK. The sequence ends in a LOADK. Any deviation and it's+ * not a list of constants.+ *+ * Here we check for this pattern almost exactly. We don't check that+ * the targets of the FORK and JUMP instructions are in the right+ * sequence.+ */+ int all_const = 1;+ int commas = 0;+ int normal = 1;+ jv a = jv_array();+ for (inst *i = expr.first; i; i = i->next) {+ if (i->op == FORK) {+ commas++;+ if (i->imm.target == NULL || i->imm.target->op != JUMP ||+ jv_array_length(jv_copy(a)) > 0) {+ normal = 0;+ break;+ }+ } else if (all_const && i->op == LOADK) {+ if (i->next != NULL && i->next->op != JUMP) {+ normal = 0;+ break;+ }+ a = jv_array_append(a, jv_copy(i->imm.constant));+ } else if (i->op != JUMP || i->imm.target == NULL ||+ i->imm.target->op != LOADK) {+ all_const = 0;+ }+ }++ if (all_const && normal &&+ (expr.last == NULL || expr.last->op == LOADK) &&+ jv_array_length(jv_copy(a)) == commas + 1) {+ block_free(expr);+ return gen_const(a);+ }++ jv_free(a);+ block b = {0,0};+ return b;+}++block gen_collect(block expr) {+ block const_array = gen_const_array(expr);+ if (const_array.first != NULL)+ return const_array;++ block array_var = gen_op_var_fresh(STOREV, "collect");+ block c = BLOCK(gen_op_simple(DUP), gen_const(jv_array()), array_var);++ block tail = BLOCK(gen_op_bound(APPEND, array_var),+ gen_op_simple(BACKTRACK));++ return BLOCK(c,+ gen_op_target(FORK, tail),+ expr,+ tail,+ gen_op_bound(LOADVN, array_var));+}++static block bind_matcher(block matcher, block body) {+ // cannot call block_bind(matcher, body) because that requires+ // block_has_only_binders(matcher), which is not true here as matchers+ // may also contain code to extract the correct elements+ for (inst* i = matcher.first; i; i = i->next) {+ if (i->op == STOREV && !i->bound_by)+ block_bind_subblock(inst_block(i), body, OP_HAS_VARIABLE, 0);+ }+ return BLOCK(matcher, body);+}++block gen_reduce(block source, block matcher, block init, block body) {+ block res_var = gen_op_var_fresh(STOREV, "reduce");+ block loop = BLOCK(gen_op_simple(DUPN),+ source,+ bind_matcher(matcher,+ BLOCK(gen_op_bound(LOADVN, res_var),+ body,+ gen_op_bound(STOREV, res_var))),+ gen_op_simple(BACKTRACK));+ return BLOCK(gen_op_simple(DUP),+ init,+ res_var,+ gen_op_target(FORK, loop),+ loop,+ gen_op_bound(LOADVN, res_var));+}++block gen_foreach(block source, block matcher, block init, block update, block extract) {+ block output = gen_op_targetlater(JUMP);+ block state_var = gen_op_var_fresh(STOREV, "foreach");+ block loop = BLOCK(gen_op_simple(DUPN),+ // get a value from the source expression:+ source,+ // destructure the value into variable(s) for all the code+ // in the body to see+ bind_matcher(matcher,+ // load the loop state variable+ BLOCK(gen_op_bound(LOADVN, state_var),+ // generate updated state+ update,+ // save the updated state for value extraction+ gen_op_simple(DUP),+ // save new state+ gen_op_bound(STOREV, state_var),+ // extract an output...+ extract,+ // ...and output it by jumping+ // past the BACKTRACK that comes+ // right after the loop body,+ // which in turn is there+ // because...+ //+ // (Incidentally, extract can also+ // backtrack, e.g., if it calls+ // empty, in which case we don't+ // get here.)+ output)));+ block foreach = BLOCK(gen_op_simple(DUP),+ init,+ state_var,+ gen_op_target(FORK, loop),+ loop,+ // ...at this point `foreach`'s original input+ // will be on top of the stack, and we don't+ // want to output it, so we backtrack.+ gen_op_simple(BACKTRACK));+ inst_set_target(output, foreach); // make that JUMP go bast the BACKTRACK at the end of the loop+ return foreach;+}++block gen_definedor(block a, block b) {+ // var found := false+ block found_var = gen_op_var_fresh(STOREV, "found");+ block init = BLOCK(gen_op_simple(DUP), gen_const(jv_false()), found_var);++ // if found, backtrack. Otherwise execute b+ block backtrack = gen_op_simple(BACKTRACK);+ block tail = BLOCK(gen_op_simple(DUP),+ gen_op_bound(LOADV, found_var),+ gen_op_target(JUMP_F, backtrack),+ backtrack,+ gen_op_simple(POP),+ b);++ // try again+ block if_notfound = gen_op_simple(BACKTRACK);++ // found := true, produce result+ block if_found = BLOCK(gen_op_simple(DUP),+ gen_const(jv_true()),+ gen_op_bound(STOREV, found_var),+ gen_op_target(JUMP, tail));++ return BLOCK(init,+ gen_op_target(FORK, if_notfound),+ a,+ gen_op_target(JUMP_F, if_found),+ if_found,+ if_notfound,+ tail);+}++int block_has_main(block top) {+ for (inst *c = top.first; c; c = c->next) {+ if (c->op == TOP)+ return 1;+ }+ return 0;+}++int block_is_funcdef(block b) {+ if (b.first != NULL && b.first->op == CLOSURE_CREATE)+ return 1;+ return 0;+}++block gen_condbranch(block iftrue, block iffalse) {+ iftrue = BLOCK(iftrue, gen_op_target(JUMP, iffalse));+ return BLOCK(gen_op_target(JUMP_F, iftrue), iftrue, iffalse);+}++block gen_and(block a, block b) {+ // a and b = if a then (if b then true else false) else false+ return BLOCK(gen_op_simple(DUP), a,+ gen_condbranch(BLOCK(gen_op_simple(POP),+ b,+ gen_condbranch(gen_const(jv_true()),+ gen_const(jv_false()))),+ BLOCK(gen_op_simple(POP), gen_const(jv_false()))));+}++block gen_or(block a, block b) {+ // a or b = if a then true else (if b then true else false)+ return BLOCK(gen_op_simple(DUP), a,+ gen_condbranch(BLOCK(gen_op_simple(POP), gen_const(jv_true())),+ BLOCK(gen_op_simple(POP),+ b,+ gen_condbranch(gen_const(jv_true()),+ gen_const(jv_false())))));+}++block gen_var_binding(block var, const char* name, block body) {+ return gen_destructure(var, gen_op_unbound(STOREV, name), body);+}++block gen_array_matcher(block left, block curr) {+ int index;+ if (block_is_noop(left))+ index = 0;+ else {+ // `left` was returned by this function, so the third inst is the+ // constant containing the previously used index+ assert(left.first->op == DUP);+ assert(left.first->next->op == SUBEXP_BEGIN);+ assert(left.first->next->next->op == LOADK);+ index = 1 + (int) jv_number_value(left.first->next->next->imm.constant);+ }++ // `left` goes at the end so that the const index is in a predictable place+ return BLOCK(gen_op_simple(DUP), gen_subexp(gen_const(jv_number(index))),+ gen_op_simple(INDEX), curr, left);+}++block gen_object_matcher(block name, block curr) {+ return BLOCK(gen_op_simple(DUP), gen_subexp(name), gen_op_simple(INDEX),+ curr);+}++block gen_destructure(block var, block matcher, block body) {+ // var bindings can be added after coding the program; leave the TOP first.+ block top = gen_noop();+ if (body.first && body.first->op == TOP)+ top = inst_block(block_take(&body));++ return BLOCK(top, gen_op_simple(DUP), var, bind_matcher(matcher, body));+}++// Like gen_var_binding(), but bind `break`'s wildcard unbound variable+static block gen_wildvar_binding(block var, const char* name, block body) {+ return BLOCK(gen_op_simple(DUP), var,+ block_bind(gen_op_unbound(STOREV, name),+ body, OP_HAS_VARIABLE | OP_BIND_WILDCARD));+}++block gen_cond(block cond, block iftrue, block iffalse) {+ return BLOCK(gen_op_simple(DUP), cond,+ gen_condbranch(BLOCK(gen_op_simple(POP), iftrue),+ BLOCK(gen_op_simple(POP), iffalse)));+}++block gen_try_handler(block handler) {+ // Quite a pain just to hide jq's internal errors.+ return gen_cond(// `if type=="object" and .__jq+ gen_and(gen_call("_equal",+ BLOCK(gen_lambda(gen_const(jv_string("object"))),+ gen_lambda(gen_noop()))),+ BLOCK(gen_subexp(gen_const(jv_string("__jq"))),+ gen_noop(),+ gen_op_simple(INDEX))),+ // `then error`+ gen_call("error", gen_noop()),+ // `else HANDLER end`+ handler);+}++block gen_try(block exp, block handler) {+ /*+ * Produce something like:+ * FORK_OPT <address of handler>+ * <exp>+ * JUMP <end of handler>+ * <handler>+ *+ * If this is not an internal try/catch, then catch and re-raise+ * internal errors to prevent them from leaking.+ *+ * The handler will only execute if we backtrack to the FORK_OPT with+ * an error (exception). If <exp> produces no value then FORK_OPT+ * will backtrack (propagate the `empty`, as it were. If <exp>+ * produces a value then we'll execute whatever bytecode follows this+ * sequence.+ */+ if (!handler.first && !handler.last)+ // A hack to deal with `.` as the handler; we could use a real NOOP here+ handler = BLOCK(gen_op_simple(DUP), gen_op_simple(POP), handler);+ exp = BLOCK(exp, gen_op_target(JUMP, handler));+ return BLOCK(gen_op_target(FORK_OPT, exp), exp, handler);+}++block gen_label(const char *label, block exp) {+ block cond = gen_call("_equal",+ BLOCK(gen_lambda(gen_noop()),+ gen_lambda(gen_op_unbound(LOADV, label))));+ return gen_wildvar_binding(gen_op_simple(GENLABEL), label,+ BLOCK(gen_op_simple(POP),+ // try exp catch if . == $label+ // then empty+ // else error end+ //+ // Can't use gen_binop(), as that's firmly+ // stuck in parser.y as it refers to things+ // like EQ.+ gen_try(exp,+ gen_cond(cond,+ gen_op_simple(BACKTRACK),+ gen_call("error", gen_noop())))));+}++block gen_cbinding(const struct cfunction* cfunctions, int ncfunctions, block code) {+ for (int cfunc=0; cfunc<ncfunctions; cfunc++) {+ inst* i = inst_new(CLOSURE_CREATE_C);+ i->imm.cfunc = &cfunctions[cfunc];+ i->symbol = strdup(i->imm.cfunc->name);+ code = block_bind(inst_block(i), code, OP_IS_CALL_PSEUDO);+ }+ return code;+}++static uint16_t nesting_level(struct bytecode* bc, inst* target) {+ uint16_t level = 0;+ assert(bc && target && target->compiled);+ while (bc && target->compiled != bc) {+ level++;+ bc = bc->parent;+ }+ assert(bc && bc == target->compiled);+ return level;+}++static int count_cfunctions(block b) {+ int n = 0;+ for (inst* i = b.first; i; i = i->next) {+ if (i->op == CLOSURE_CREATE_C) n++;+ n += count_cfunctions(i->subfn);+ }+ return n;+}+++// Expands call instructions into a calling sequence+static int expand_call_arglist(block* b) {+ int errors = 0;+ block ret = gen_noop();+ for (inst* curr; (curr = block_take(b));) {+ if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {+ if (!curr->bound_by) {+ if (curr->symbol[0] == '*' && curr->symbol[1] >= '1' && curr->symbol[1] <= '3' && curr->symbol[2] == '\0')+ locfile_locate(curr->locfile, curr->source, "jq: error: break used outside labeled control structure");+ else+ locfile_locate(curr->locfile, curr->source, "jq: error: %s/%d is not defined", curr->symbol, block_count_actuals(curr->arglist));+ errors++;+ // don't process this instruction if it's not well-defined+ ret = BLOCK(ret, inst_block(curr));+ continue;+ }+ }++ block prelude = gen_noop();+ if (curr->op == CALL_JQ) {+ int actual_args = 0, desired_args = 0;+ // We expand the argument list as a series of instructions+ switch (curr->bound_by->op) {+ default: assert(0 && "Unknown function type"); break;+ case CLOSURE_CREATE:+ case CLOSURE_PARAM: {+ block callargs = gen_noop();+ for (inst* i; (i = block_take(&curr->arglist));) {+ assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO);+ block b = inst_block(i);+ switch (i->op) {+ default: assert(0 && "Unknown type of parameter"); break;+ case CLOSURE_REF:+ block_append(&callargs, b);+ break;+ case CLOSURE_CREATE:+ block_append(&prelude, b);+ block_append(&callargs, gen_op_bound(CLOSURE_REF, b));+ break;+ }+ actual_args++;+ }+ curr->imm.intval = actual_args;+ curr->arglist = callargs;++ if (curr->bound_by->op == CLOSURE_CREATE) {+ for (inst* i = curr->bound_by->arglist.first; i; i = i->next) {+ assert(i->op == CLOSURE_PARAM);+ desired_args++;+ }+ }+ break;+ }++ case CLOSURE_CREATE_C: {+ for (inst* i; (i = block_take(&curr->arglist)); ) {+ assert(i->op == CLOSURE_CREATE); // FIXME+ block body = i->subfn;+ i->subfn = gen_noop();+ inst_free(i);+ // arguments should be pushed in reverse order, prepend them to prelude+ errors += expand_call_arglist(&body);+ prelude = BLOCK(gen_subexp(body), prelude);+ actual_args++;+ }+ assert(curr->op == CALL_JQ);+ curr->op = CALL_BUILTIN;+ curr->imm.intval = actual_args + 1 /* include the implicit input in arg count */;+ assert(curr->bound_by->op == CLOSURE_CREATE_C);+ desired_args = curr->bound_by->imm.cfunc->nargs - 1;+ assert(!curr->arglist.first);+ break;+ }+ }++ assert(actual_args == desired_args); // because now handle this above+ }+ ret = BLOCK(ret, prelude, inst_block(curr));+ }+ *b = ret;+ return errors;+}++static int compile(struct bytecode* bc, block b, struct locfile* lf) {+ int errors = 0;+ int pos = 0;+ int var_frame_idx = 0;+ bc->nsubfunctions = 0;+ errors += expand_call_arglist(&b);+ b = BLOCK(b, gen_op_simple(RET));+ jv localnames = jv_array();+ for (inst* curr = b.first; curr; curr = curr->next) {+ if (!curr->next) assert(curr == b.last);+ int length = opcode_describe(curr->op)->length;+ if (curr->op == CALL_JQ) {+ for (inst* arg = curr->arglist.first; arg; arg = arg->next) {+ length += 2;+ }+ }+ pos += length;+ curr->bytecode_pos = pos;+ curr->compiled = bc;++ assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);++ if ((opcode_describe(curr->op)->flags & OP_HAS_VARIABLE) &&+ curr->bound_by == curr) {+ curr->imm.intval = var_frame_idx++;+ localnames = jv_array_append(localnames, jv_string(curr->symbol));+ }++ if (curr->op == CLOSURE_CREATE) {+ assert(curr->bound_by == curr);+ curr->imm.intval = bc->nsubfunctions++;+ }+ if (curr->op == CLOSURE_CREATE_C) {+ assert(curr->bound_by == curr);+ int idx = bc->globals->ncfunctions++;+ bc->globals->cfunc_names = jv_array_append(bc->globals->cfunc_names,+ jv_string(curr->symbol));+ bc->globals->cfunctions[idx] = *curr->imm.cfunc;+ curr->imm.intval = idx;+ }+ }+ if (pos > 0xFFFF) {+ // too long for program counter to fit in uint16_t+ locfile_locate(lf, UNKNOWN_LOCATION,+ "function compiled to %d bytes which is too long", pos);+ errors++;+ }+ bc->codelen = pos;+ bc->debuginfo = jv_object_set(bc->debuginfo, jv_string("locals"), localnames);+ if (bc->nsubfunctions) {+ bc->subfunctions = jv_mem_alloc(sizeof(struct bytecode*) * bc->nsubfunctions);+ for (inst* curr = b.first; curr; curr = curr->next) {+ if (curr->op == CLOSURE_CREATE) {+ struct bytecode* subfn = jv_mem_alloc(sizeof(struct bytecode));+ bc->subfunctions[curr->imm.intval] = subfn;+ subfn->globals = bc->globals;+ subfn->parent = bc;+ subfn->nclosures = 0;+ subfn->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_string(curr->symbol));+ jv params = jv_array();+ for (inst* param = curr->arglist.first; param; param = param->next) {+ assert(param->op == CLOSURE_PARAM);+ assert(param->bound_by == param);+ param->imm.intval = subfn->nclosures++;+ param->compiled = subfn;+ params = jv_array_append(params, jv_string(param->symbol));+ }+ subfn->debuginfo = jv_object_set(subfn->debuginfo, jv_string("params"), params);+ errors += compile(subfn, curr->subfn, lf);+ curr->subfn = gen_noop();+ }+ }+ } else {+ bc->subfunctions = 0;+ }+ uint16_t* code = jv_mem_alloc(sizeof(uint16_t) * bc->codelen);+ bc->code = code;+ pos = 0;+ jv constant_pool = jv_array();+ int maxvar = -1;+ if (!errors) for (inst* curr = b.first; curr; curr = curr->next) {+ const struct opcode_description* op = opcode_describe(curr->op);+ if (op->length == 0)+ continue;+ code[pos++] = curr->op;+ assert(curr->op != CLOSURE_REF && curr->op != CLOSURE_PARAM);+ if (curr->op == CALL_BUILTIN) {+ assert(curr->bound_by->op == CLOSURE_CREATE_C);+ assert(!curr->arglist.first);+ code[pos++] = (uint16_t)curr->imm.intval;+ code[pos++] = curr->bound_by->imm.intval;+ } else if (curr->op == CALL_JQ) {+ assert(curr->bound_by->op == CLOSURE_CREATE ||+ curr->bound_by->op == CLOSURE_PARAM);+ code[pos++] = (uint16_t)curr->imm.intval;+ code[pos++] = nesting_level(bc, curr->bound_by);+ code[pos++] = curr->bound_by->imm.intval |+ (curr->bound_by->op == CLOSURE_CREATE ? ARG_NEWCLOSURE : 0);+ for (inst* arg = curr->arglist.first; arg; arg = arg->next) {+ assert(arg->op == CLOSURE_REF && arg->bound_by->op == CLOSURE_CREATE);+ code[pos++] = nesting_level(bc, arg->bound_by);+ code[pos++] = arg->bound_by->imm.intval | ARG_NEWCLOSURE;+ }+ } else if ((op->flags & OP_HAS_CONSTANT) && (op->flags & OP_HAS_VARIABLE)) {+ // STORE_GLOBAL: constant global, basically+ code[pos++] = jv_array_length(jv_copy(constant_pool));+ constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));+ code[pos++] = nesting_level(bc, curr->bound_by);+ uint16_t var = (uint16_t)curr->bound_by->imm.intval;+ code[pos++] = var;+ } else if (op->flags & OP_HAS_CONSTANT) {+ code[pos++] = jv_array_length(jv_copy(constant_pool));+ constant_pool = jv_array_append(constant_pool, jv_copy(curr->imm.constant));+ } else if (op->flags & OP_HAS_VARIABLE) {+ code[pos++] = nesting_level(bc, curr->bound_by);+ uint16_t var = (uint16_t)curr->bound_by->imm.intval;+ code[pos++] = var;+ if (var > maxvar) maxvar = var;+ } else if (op->flags & OP_HAS_BRANCH) {+ assert(curr->imm.target->bytecode_pos != -1);+ assert(curr->imm.target->bytecode_pos > pos); // only forward branches+ code[pos] = curr->imm.target->bytecode_pos - (pos + 1);+ pos++;+ } else if (op->length > 1) {+ assert(0 && "codegen not implemented for this operation");+ }+ }+ bc->constants = constant_pool;+ bc->nlocals = maxvar + 2; // FIXME: frames of size zero?+ block_free(b);+ return errors;+}++int block_compile(block b, struct bytecode** out, struct locfile* lf) {+ struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode));+ bc->parent = 0;+ bc->nclosures = 0;+ bc->globals = jv_mem_alloc(sizeof(struct symbol_table));+ int ncfunc = count_cfunctions(b);+ bc->globals->ncfunctions = 0;+ bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc);+ bc->globals->cfunc_names = jv_array();+ bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());+ int nerrors = compile(bc, b, lf);+ assert(bc->globals->ncfunctions == ncfunc);+ if (nerrors > 0) {+ bytecode_free(bc);+ *out = 0;+ } else {+ *out = bc;+ }+ return nerrors;+}++void block_free(block b) {+ struct inst* next;+ for (struct inst* curr = b.first; curr; curr = next) {+ next = curr->next;+ inst_free(curr);+ }+}
@@ -0,0 +1,102 @@+#ifndef COMPILE_H+#define COMPILE_H+#include <stdint.h>+#include "jv.h"+#include "bytecode.h"+#include "locfile.h"++struct inst;+typedef struct inst inst;+++typedef struct block {+ inst* first;+ inst* last;+} block;++block gen_location(location, struct locfile*, block);++block gen_noop();+int block_is_noop(block b);+block gen_op_simple(opcode op);+block gen_const(jv constant);+block gen_const_global(jv constant, const char *name);+int block_is_const(block b);+int block_is_const_inf(block b);+jv_kind block_const_kind(block b);+jv block_const(block b);+block gen_op_target(opcode op, block target);+block gen_op_unbound(opcode op, const char* name);+block gen_op_bound(opcode op, block binder);+block gen_op_var_fresh(opcode op, const char* name);++block gen_module(block metadata);+jv block_module_meta(block b);+block gen_import(const char* name, const char *as, int is_data);+block gen_import_meta(block import, block metadata);+block gen_function(const char* name, block formals, block body);+block gen_param_regular(const char* name);+block gen_param(const char* name);+block gen_lambda(block body);+block gen_call(const char* name, block body);+block gen_subexp(block a);+block gen_both(block a, block b);+block gen_const_object(block expr);+block gen_collect(block expr);+block gen_reduce(block source, block matcher, block init, block body);+block gen_foreach(block source, block matcher, block init, block update, block extract);+block gen_definedor(block a, block b);+block gen_condbranch(block iftrue, block iffalse);+block gen_and(block a, block b);+block gen_or(block a, block b);++block gen_var_binding(block var, const char* name, block body);+block gen_array_matcher(block left, block curr);+block gen_object_matcher(block name, block curr);+block gen_destructure(block var, block matcher, block body);++block gen_cond(block cond, block iftrue, block iffalse);+block gen_try_handler(block handler);+block gen_try(block exp, block handler);+block gen_label(const char *label, block exp);++block gen_cbinding(const struct cfunction* functions, int nfunctions, block b);++void block_append(block* b, block b2);+block block_join(block a, block b);+int block_has_only_binders_and_imports(block, int bindflags);+int block_has_only_binders(block, int bindflags);+int block_has_main(block);+int block_is_funcdef(block b);+int block_is_single(block b);+block block_bind(block binder, block body, int bindflags);+block block_bind_library(block binder, block body, int bindflags, const char* libname);+block block_bind_referenced(block binder, block body, int bindflags);+block block_drop_unreferenced(block body);++jv block_take_imports(block* body);++int block_compile(block, struct bytecode**, struct locfile*);++void block_free(block);++++// Here's some horrible preprocessor gunk so that code+// sequences can be contructed as BLOCK(block1, block2, block3)++#define BLOCK_1(b1) (b1)+#define BLOCK_2(b1,b2) (block_join((b1),(b2)))+#define BLOCK_3(b1,b2,b3) (block_join(BLOCK_2(b1,b2),(b3)))+#define BLOCK_4(b1,b2,b3,b4) (block_join(BLOCK_3(b1,b2,b3),(b4)))+#define BLOCK_5(b1,b2,b3,b4,b5) (block_join(BLOCK_4(b1,b2,b3,b4),(b5)))+#define BLOCK_6(b1,b2,b3,b4,b5,b6) (block_join(BLOCK_5(b1,b2,b3,b4,b5),(b6)))+#define BLOCK_7(b1,b2,b3,b4,b5,b6,b7) (block_join(BLOCK_6(b1,b2,b3,b4,b5,b6),(b7)))+#define BLOCK_8(b1,b2,b3,b4,b5,b6,b7,b8) (block_join(BLOCK_7(b1,b2,b3,b4,b5,b6,b7),(b8)))++#define BLOCK_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME+#define BLOCK(...) \+ BLOCK_IDX(__VA_ARGS__, BLOCK_8, BLOCK_7, BLOCK_6, BLOCK_5, BLOCK_4, BLOCK_3, BLOCK_2, BLOCK_1)(__VA_ARGS__)+++#endif
@@ -0,0 +1,112 @@+#ifndef EXEC_STACK_H+#define EXEC_STACK_H+#include <stddef.h>+#include <stdint.h>+#include <stdlib.h>+#include <string.h>+#include "jv_alloc.h"++/*+ * The stack is a directed forest of variably sized blocks. Each block has a+ * "next" block which is at a higher memory address, or 0 if the block has no+ * "next" block. More than one block may have no "next" block. A block may be+ * the "next" block of more than one other block. Pushed blocks are added at+ * the low-address end of the stack.+ *+ * Stack pointers are negative integers that are offsets relative to "mem_end",+ * the end of the allocated region. The stack "bound" is the stack pointer of+ * the last block that would be able to fit in the currently allocated region.+ * The stack "limit" is the stack pointer of the last block currently in the+ * stack. The stack pointer of the "next" block is stored directly below each+ * block.+ *+ * <- mem_end = 0x100+ * 0xF8 +------------++ * 0xF0 | |+ * 0xE8 +------------+ <- stack_ptr1 = -0x18+ * 0xE0 next = 0+ * 0xD8 +------------++ * 0xD0 | |+ * 0xC8 | |+ * 0xC0 +------------+ <- stack_ptr2 = limit = -0x40+ * 0xB8 next = -0x18+ * 0xB0+ * 0xA8 <- bound = -0x58+ * 0xA0+ */++struct determine_alignment {+ char x;+ union { int i; double d; uint64_t u64; size_t sz; void* ptr; } u;+};+enum {ALIGNMENT = offsetof(struct determine_alignment, u)};++static size_t align_round_up(size_t sz) {+ return ((sz + (ALIGNMENT - 1)) / ALIGNMENT) * ALIGNMENT;+}++typedef int stack_ptr;++struct stack {+ char* mem_end; // one-past-the-end of allocated region+ stack_ptr bound;+ stack_ptr limit; // 0 - stack is empty+};++static void stack_init(struct stack* s) {+ s->mem_end = 0;+ s->bound = ALIGNMENT;+ s->limit = 0;+}++static void stack_reset(struct stack* s) {+ assert(s->limit == 0 && "stack freed while not empty");+ char* mem_start = s->mem_end - ( -s->bound + ALIGNMENT);+ free(mem_start);+ stack_init(s);+}++static int stack_pop_will_free(struct stack* s, stack_ptr p) {+ return p == s->limit;+}++static void* stack_block(struct stack* s, stack_ptr p) {+ return (void*)(s->mem_end + p);+}++static stack_ptr* stack_block_next(struct stack* s, stack_ptr p) {+ return &((stack_ptr*)stack_block(s, p))[-1];+}++static void stack_reallocate(struct stack* s, size_t sz) {+ int old_mem_length = -(s->bound) + ALIGNMENT;+ char* old_mem_start = s->mem_end - old_mem_length;++ int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2);+ char* new_mem_start = jv_mem_realloc(old_mem_start, new_mem_length);+ memmove(new_mem_start + (new_mem_length - old_mem_length),+ new_mem_start, old_mem_length);+ s->mem_end = new_mem_start + new_mem_length;+ s->bound = -(new_mem_length - ALIGNMENT);+}++static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) {+ int alloc_sz = align_round_up(sz) + ALIGNMENT;+ stack_ptr r = s->limit - alloc_sz;+ if (r < s->bound) {+ stack_reallocate(s, alloc_sz);+ }+ s->limit = r;+ *stack_block_next(s, r) = p;+ return r;+}++static stack_ptr stack_pop_block(struct stack* s, stack_ptr p, size_t sz) {+ stack_ptr r = *stack_block_next(s, p);+ if (p == s->limit) {+ int alloc_sz = align_round_up(sz) + ALIGNMENT;+ s->limit += alloc_sz;+ }+ return r;+}+#endif
@@ -0,0 +1,1155 @@+#include <assert.h>+#include <errno.h>+#include <stdarg.h>+#include <stdio.h>+#include <stdlib.h>+#include <stdint.h>+#include <sys/stat.h>++#include "exec_stack.h"+#include "bytecode.h"++#include "jv_alloc.h"+#include "jq_parser.h"+#include "locfile.h"+#include "jv.h"+#include "jq.h"+#include "parser.h"+#include "builtin.h"+#include "util.h"+#include "linker.h"++struct jq_state {+ void (*nomem_handler)(void *);+ void *nomem_handler_data;+ struct bytecode* bc;++ jq_msg_cb err_cb;+ void *err_cb_data;+ jv error;++ struct stack stk;+ stack_ptr curr_frame;+ stack_ptr stk_top;+ stack_ptr fork_top;++ jv path;+ jv value_at_path;+ int subexp_nest;+ int debug_trace_enabled;+ int initial_execution;+ unsigned next_label;++ jv attrs;+ jq_input_cb input_cb;+ void *input_cb_data;+ jq_msg_cb debug_cb;+ void *debug_cb_data;+};++struct closure {+ struct bytecode* bc; // jq bytecode+ stack_ptr env; // jq stack address of closed frame+};++// locals for any function called: either a closure or a local variable+union frame_entry {+ struct closure closure;+ jv localvar;+};++// jq function call frame+struct frame {+ struct bytecode* bc; // jq bytecode for callee+ stack_ptr env; // jq stack address of frame to return to+ stack_ptr retdata; // jq stack address to unwind to on RET+ uint16_t* retaddr; // jq bytecode return address+ union frame_entry entries[]; // nclosures + nlocals+};++static int frame_size(struct bytecode* bc) {+ return sizeof(struct frame) + sizeof(union frame_entry) * (bc->nclosures + bc->nlocals);+}++static struct frame* frame_current(struct jq_state* jq) {+ struct frame* fp = stack_block(&jq->stk, jq->curr_frame);++ stack_ptr next = *stack_block_next(&jq->stk, jq->curr_frame);+ if (next) {+ struct frame* fpnext = stack_block(&jq->stk, next);+ struct bytecode* bc = fpnext->bc;+ assert(fp->retaddr >= bc->code && fp->retaddr < bc->code + bc->codelen);+ } else {+ assert(fp->retaddr == 0);+ }+ return fp;+}++static stack_ptr frame_get_level(struct jq_state* jq, int level) {+ stack_ptr fr = jq->curr_frame;+ for (int i=0; i<level; i++) {+ struct frame* fp = stack_block(&jq->stk, fr);+ fr = fp->env;+ }+ return fr;+}++static jv* frame_local_var(struct jq_state* jq, int var, int level) {+ struct frame* fr = stack_block(&jq->stk, frame_get_level(jq, level));+ assert(var >= 0);+ assert(var < fr->bc->nlocals);+ return &fr->entries[fr->bc->nclosures + var].localvar;+}++static struct closure make_closure(struct jq_state* jq, uint16_t* pc) {+ uint16_t level = *pc++;+ uint16_t idx = *pc++;+ stack_ptr fridx = frame_get_level(jq, level);+ struct frame* fr = stack_block(&jq->stk, fridx);+ if (idx & ARG_NEWCLOSURE) {+ // A new closure closing the frame identified by level, and with+ // the bytecode body of the idx'th subfunction of that frame+ int subfn_idx = idx & ~ARG_NEWCLOSURE;+ assert(subfn_idx < fr->bc->nsubfunctions);+ struct closure cl = {fr->bc->subfunctions[subfn_idx],+ fridx};+ return cl;+ } else {+ // A reference to a closure from the frame identified by level; copy+ // it as-is+ int closure = idx;+ assert(closure >= 0);+ assert(closure < fr->bc->nclosures);+ return fr->entries[closure].closure;+ }+}++static struct frame* frame_push(struct jq_state* jq, struct closure callee,+ uint16_t* argdef, int nargs) {+ stack_ptr new_frame_idx = stack_push_block(&jq->stk, jq->curr_frame, frame_size(callee.bc));+ struct frame* new_frame = stack_block(&jq->stk, new_frame_idx);+ new_frame->bc = callee.bc;+ new_frame->env = callee.env;+ assert(nargs == new_frame->bc->nclosures);+ union frame_entry* entries = new_frame->entries;+ for (int i=0; i<nargs; i++) {+ entries->closure = make_closure(jq, argdef + i * 2);+ entries++;+ }+ for (int i=0; i<callee.bc->nlocals; i++) {+ entries->localvar = jv_invalid();+ entries++;+ }+ jq->curr_frame = new_frame_idx;+ return new_frame;+}++static void frame_pop(struct jq_state* jq) {+ assert(jq->curr_frame);+ struct frame* fp = frame_current(jq);+ if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {+ int nlocals = fp->bc->nlocals;+ for (int i=0; i<nlocals; i++) {+ jv_free(*frame_local_var(jq, i, 0));+ }+ }+ jq->curr_frame = stack_pop_block(&jq->stk, jq->curr_frame, frame_size(fp->bc));+}++void stack_push(jq_state *jq, jv val) {+ assert(jv_is_valid(val));+ jq->stk_top = stack_push_block(&jq->stk, jq->stk_top, sizeof(jv));+ jv* sval = stack_block(&jq->stk, jq->stk_top);+ *sval = val;+}++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;+}++// Like stack_pop(), but assert !stack_pop_will_free() and replace with+// jv_null() on the stack.+jv stack_popn(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)) {+ *sval = jv_null();+ }+ jq->stk_top = stack_pop_block(&jq->stk, jq->stk_top, sizeof(jv));+ assert(jv_is_valid(val));+ return val;+}+++struct forkpoint {+ stack_ptr saved_data_stack;+ stack_ptr saved_curr_frame;+ int path_len, subexp_nest;+ jv value_at_path;+ uint16_t* return_address;+};++struct stack_pos {+ stack_ptr saved_data_stack, saved_curr_frame;+};++struct stack_pos stack_get_pos(jq_state* jq) {+ struct stack_pos sp = {jq->stk_top, jq->curr_frame};+ return sp;+}++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->value_at_path = jv_copy(jq->value_at_path);+ 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 int path_intact(jq_state *jq, jv curr) {+ if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {+ return jv_identical(curr, jv_copy(jq->value_at_path));+ } else {+ jv_free(curr);+ return 1;+ }+}++static void path_append(jq_state* jq, jv component, jv value_at_path) {+ if (jq->subexp_nest == 0 && jv_get_kind(jq->path) == JV_KIND_ARRAY) {+ int n1 = jv_array_length(jv_copy(jq->path));+ jq->path = jv_array_append(jq->path, component);+ int n2 = jv_array_length(jv_copy(jq->path));+ assert(n2 == n1 + 1);+ jv_free(jq->value_at_path);+ jq->value_at_path = value_at_path;+ } else {+ jv_free(component);+ jv_free(value_at_path);+ }+}++uint16_t* stack_restore(jq_state *jq){+ while (!stack_pop_will_free(&jq->stk, jq->fork_top)) {+ if (stack_pop_will_free(&jq->stk, jq->stk_top)) {+ jv_free(stack_pop(jq));+ } else if (stack_pop_will_free(&jq->stk, jq->curr_frame)) {+ frame_pop(jq);+ } else {+ assert(0);+ }+ }++ if (jq->fork_top == 0) {+ return 0;+ }++ struct forkpoint* fork = stack_block(&jq->stk, jq->fork_top);+ uint16_t* retaddr = fork->return_address;+ jq->stk_top = fork->saved_data_stack;+ jq->curr_frame = fork->saved_curr_frame;+ int path_len = fork->path_len;+ if (jv_get_kind(jq->path) == JV_KIND_ARRAY) {+ assert(path_len >= 0);+ jq->path = jv_array_slice(jq->path, 0, path_len);+ } else {+ assert(path_len == 0);+ }+ jv_free(jq->value_at_path);+ jq->value_at_path = fork->value_at_path;+ jq->subexp_nest = fork->subexp_nest;+ jq->fork_top = stack_pop_block(&jq->stk, jq->fork_top, sizeof(struct forkpoint));+ return retaddr;+}++static void jq_reset(jq_state *jq) {+ while (stack_restore(jq)) {}++ assert(jq->stk_top == 0);+ assert(jq->fork_top == 0);+ assert(jq->curr_frame == 0);+ stack_reset(&jq->stk);+ jv_free(jq->error);+ jq->error = jv_null();++ if (jv_get_kind(jq->path) != JV_KIND_INVALID)+ jv_free(jq->path);+ jq->path = jv_null();+ jv_free(jq->value_at_path);+ jq->value_at_path = jv_null();+ jq->subexp_nest = 0;+}++void jq_report_error(jq_state *jq, jv value) {+ assert(jq->err_cb);+ // callback must jv_free() its jv argument+ jq->err_cb(jq->err_cb_data, value);+}++static void set_error(jq_state *jq, jv value) {+ // Record so try/catch can find it.+ jv_free(jq->error);+ jq->error = value;+}++#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)++jv jq_next(jq_state *jq) {+ jv cfunc_input[MAX_CFUNCTION_ARGS];++ jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);++ uint16_t* pc = stack_restore(jq);+ assert(pc);++ int raising;+ int backtracking = !jq->initial_execution;+ jq->initial_execution = 0;+ assert(jv_get_kind(jq->error) == JV_KIND_NULL);+ while (1) {+ uint16_t opcode = *pc;+ raising = 0;++ if (jq->debug_trace_enabled) {+ dump_operation(frame_current(jq)->bc, pc);+ printf("\t");+ const struct opcode_description* opdesc = opcode_describe(opcode);+ stack_ptr param = 0;+ if (!backtracking) {+ int stack_in = opdesc->stack_in;+ if (stack_in == -1) stack_in = pc[1];+ for (int i=0; i<stack_in; i++) {+ if (i == 0) {+ param = jq->stk_top;+ } else {+ printf(" | ");+ param = *stack_block_next(&jq->stk, param);+ }+ if (!param) break;+ jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), JV_PRINT_REFCOUNT);+ //printf("<%d>", jv_get_refcnt(param->val));+ //printf(" -- ");+ //jv_dump(jv_copy(jq->path), 0);+ }+ } else {+ printf("\t<backtracking>");+ }++ printf("\n");+ }++ if (backtracking) {+ opcode = ON_BACKTRACK(opcode);+ backtracking = 0;+ raising = !jv_is_valid(jq->error);+ }+ pc++;++ switch (opcode) {+ default: assert(0 && "invalid instruction");++ case TOP: break;++ case LOADK: {+ jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);+ assert(jv_is_valid(v));+ jv_free(stack_pop(jq));+ stack_push(jq, v);+ break;+ }++ case GENLABEL: {+ stack_push(jq, JV_OBJECT(jv_string("__jq"), jv_number(jq->next_label++)));+ break;+ }++ case DUP: {+ jv v = stack_pop(jq);+ stack_push(jq, jv_copy(v));+ stack_push(jq, v);+ break;+ }++ case DUPN: {+ jv v = stack_popn(jq);+ stack_push(jq, jv_copy(v));+ stack_push(jq, v);+ break;+ }++ case DUP2: {+ jv keep = stack_pop(jq);+ jv v = stack_pop(jq);+ stack_push(jq, jv_copy(v));+ stack_push(jq, keep);+ stack_push(jq, v);+ break;+ }++ case SUBEXP_BEGIN: {+ jv v = stack_pop(jq);+ stack_push(jq, jv_copy(v));+ stack_push(jq, v);+ jq->subexp_nest++;+ break;+ }++ case SUBEXP_END: {+ assert(jq->subexp_nest > 0);+ jq->subexp_nest--;+ jv a = stack_pop(jq);+ jv b = stack_pop(jq);+ stack_push(jq, a);+ stack_push(jq, b);+ break;+ }++ case POP: {+ jv_free(stack_pop(jq));+ break;+ }++ case APPEND: {+ jv v = stack_pop(jq);+ uint16_t level = *pc++;+ uint16_t vidx = *pc++;+ jv* var = frame_local_var(jq, vidx, level);+ assert(jv_get_kind(*var) == JV_KIND_ARRAY);+ *var = jv_array_append(*var, v);+ break;+ }++ case INSERT: {+ jv stktop = stack_pop(jq);+ jv v = stack_pop(jq);+ jv k = stack_pop(jq);+ jv objv = stack_pop(jq);+ assert(jv_get_kind(objv) == JV_KIND_OBJECT);+ if (jv_get_kind(k) == JV_KIND_STRING) {+ stack_push(jq, jv_object_set(objv, k, v));+ stack_push(jq, stktop);+ } else {+ char errbuf[15];+ set_error(jq, jv_invalid_with_msg(jv_string_fmt("Cannot use %s (%s) as object key",+ jv_kind_name(jv_get_kind(k)),+ jv_dump_string_trunc(jv_copy(k), errbuf, sizeof(errbuf)))));+ jv_free(stktop);+ jv_free(v);+ jv_free(k);+ jv_free(objv);+ goto do_backtrack;+ }+ break;+ }++ case ON_BACKTRACK(RANGE):+ case RANGE: {+ uint16_t level = *pc++;+ uint16_t v = *pc++;+ jv* var = frame_local_var(jq, v, level);+ jv max = stack_pop(jq);+ if (raising) 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))) {+ /* finished iterating */+ goto do_backtrack;+ } else {+ jv curr = jv_copy(*var);+ *var = jv_number(jv_number_value(*var) + 1);++ struct stack_pos spos = stack_get_pos(jq);+ stack_push(jq, jv_copy(max));+ stack_save(jq, pc - 3, spos);++ stack_push(jq, curr);+ }+ break;+ }++ // FIXME: loadv/storev may do too much copying/freeing+ case LOADV: {+ uint16_t level = *pc++;+ uint16_t v = *pc++;+ jv* var = frame_local_var(jq, v, level);+ if (jq->debug_trace_enabled) {+ printf("V%d = ", v);+ jv_dump(jv_copy(*var), 0);+ printf(" (%d)\n", jv_get_refcnt(*var));+ }+ jv_free(stack_pop(jq));+ stack_push(jq, jv_copy(*var));+ break;+ }++ // Does a load but replaces the variable with null+ case LOADVN: {+ uint16_t level = *pc++;+ uint16_t v = *pc++;+ jv* var = frame_local_var(jq, v, level);+ if (jq->debug_trace_enabled) {+ printf("V%d = ", v);+ jv_dump(jv_copy(*var), 0);+ printf(" (%d)\n", jv_get_refcnt(*var));+ }+ jv_free(stack_popn(jq));+ stack_push(jq, *var);+ *var = jv_null();+ break;+ }++ case STOREV: {+ uint16_t level = *pc++;+ uint16_t v = *pc++;+ jv* var = frame_local_var(jq, v, level);+ jv val = stack_pop(jq);+ if (jq->debug_trace_enabled) {+ printf("V%d = ", v);+ jv_dump(jv_copy(val), 0);+ printf(" (%d)\n", jv_get_refcnt(val));+ }+ jv_free(*var);+ *var = val;+ break;+ }++ case STORE_GLOBAL: {+ // Get the constant+ jv val = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);+ assert(jv_is_valid(val));++ // Store the var+ uint16_t level = *pc++;+ uint16_t v = *pc++;+ jv* var = frame_local_var(jq, v, level);+ if (jq->debug_trace_enabled) {+ printf("V%d = ", v);+ jv_dump(jv_copy(val), 0);+ printf(" (%d)\n", jv_get_refcnt(val));+ }+ jv_free(*var);+ *var = val;+ break;+ }++ case PATH_BEGIN: {+ jv v = stack_pop(jq);+ stack_push(jq, jq->path);++ stack_save(jq, pc - 1, stack_get_pos(jq));++ stack_push(jq, jv_number(jq->subexp_nest));+ stack_push(jq, jq->value_at_path);+ stack_push(jq, jv_copy(v));++ jq->path = jv_array();+ jq->value_at_path = v; // next INDEX operation must index into v+ jq->subexp_nest = 0;+ break;+ }++ case PATH_END: {+ jv v = stack_pop(jq);+ // detect invalid path expression like path(.a | reverse)+ if (!path_intact(jq, jv_copy(v))) {+ char errbuf[30];+ jv msg = jv_string_fmt(+ "Invalid path expression with result %s",+ jv_dump_string_trunc(v, errbuf, sizeof(errbuf)));+ set_error(jq, jv_invalid_with_msg(msg));+ goto do_backtrack;+ }+ jv_free(v); // discard value, only keep path++ jv old_value_at_path = stack_pop(jq);+ int old_subexp_nest = (int)jv_number_value(stack_pop(jq));++ jv path = jq->path;+ jq->path = stack_pop(jq);++ struct stack_pos spos = stack_get_pos(jq);+ stack_push(jq, jv_copy(path));+ stack_save(jq, pc - 1, spos);++ stack_push(jq, path);+ jq->subexp_nest = old_subexp_nest;+ jv_free(jq->value_at_path);+ jq->value_at_path = old_value_at_path;+ break;+ }++ case ON_BACKTRACK(PATH_BEGIN):+ case ON_BACKTRACK(PATH_END): {+ jv_free(jq->path);+ jq->path = stack_pop(jq);+ goto do_backtrack;+ }++ case INDEX:+ case INDEX_OPT: {+ jv t = stack_pop(jq);+ jv k = stack_pop(jq);+ // detect invalid path expression like path(reverse | .a)+ if (!path_intact(jq, jv_copy(t))) {+ char keybuf[15];+ char objbuf[30];+ jv msg = jv_string_fmt(+ "Invalid path expression near attempt to access element %s of %s",+ jv_dump_string_trunc(k, keybuf, sizeof(keybuf)),+ jv_dump_string_trunc(t, objbuf, sizeof(objbuf)));+ set_error(jq, jv_invalid_with_msg(msg));+ goto do_backtrack;+ }+ jv v = jv_get(t, jv_copy(k));+ if (jv_is_valid(v)) {+ path_append(jq, k, jv_copy(v));+ stack_push(jq, v);+ } else {+ jv_free(k);+ if (opcode == INDEX)+ set_error(jq, v);+ else+ jv_free(v);+ goto do_backtrack;+ }+ break;+ }+++ case JUMP: {+ uint16_t offset = *pc++;+ pc += offset;+ break;+ }++ case JUMP_F: {+ uint16_t offset = *pc++;+ jv t = stack_pop(jq);+ jv_kind kind = jv_get_kind(t);+ if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {+ pc += offset;+ }+ stack_push(jq, t); // FIXME do this better+ break;+ }++ case EACH:+ case EACH_OPT: {+ jv container = stack_pop(jq);+ // detect invalid path expression like path(reverse | .[])+ if (!path_intact(jq, jv_copy(container))) {+ char errbuf[30];+ jv msg = jv_string_fmt(+ "Invalid path expression near attempt to iterate through %s",+ jv_dump_string_trunc(container, errbuf, sizeof(errbuf)));+ set_error(jq, jv_invalid_with_msg(msg));+ goto do_backtrack;+ }+ stack_push(jq, container);+ stack_push(jq, jv_number(-1));+ // fallthrough+ }+ case ON_BACKTRACK(EACH):+ case ON_BACKTRACK(EACH_OPT): {+ int idx = jv_number_value(stack_pop(jq));+ jv container = stack_pop(jq);++ int keep_going, is_last = 0;+ jv key, value;+ if (jv_get_kind(container) == JV_KIND_ARRAY) {+ if (opcode == EACH || opcode == EACH_OPT) idx = 0;+ else idx = idx + 1;+ int len = jv_array_length(jv_copy(container));+ keep_going = idx < len;+ is_last = idx == len - 1;+ if (keep_going) {+ key = jv_number(idx);+ value = jv_array_get(jv_copy(container), idx);+ }+ } else if (jv_get_kind(container) == JV_KIND_OBJECT) {+ if (opcode == EACH || opcode == EACH_OPT) idx = jv_object_iter(container);+ else idx = jv_object_iter_next(container, idx);+ keep_going = jv_object_iter_valid(container, idx);+ if (keep_going) {+ key = jv_object_iter_key(container, idx);+ value = jv_object_iter_value(container, idx);+ }+ } else {+ assert(opcode == EACH || opcode == EACH_OPT);+ if (opcode == EACH) {+ char errbuf[15];+ set_error(jq,+ jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s (%s)",+ jv_kind_name(jv_get_kind(container)),+ jv_dump_string_trunc(jv_copy(container), errbuf, sizeof(errbuf)))));+ }+ keep_going = 0;+ }++ if (!keep_going || raising) {+ if (keep_going)+ jv_free(value);+ jv_free(container);+ goto do_backtrack;+ } else if (is_last) {+ // we don't need to make a backtrack point+ jv_free(container);+ path_append(jq, key, jv_copy(value));+ stack_push(jq, value);+ } else {+ struct stack_pos spos = stack_get_pos(jq);+ stack_push(jq, container);+ stack_push(jq, jv_number(idx));+ stack_save(jq, pc - 1, spos);+ path_append(jq, key, jv_copy(value));+ stack_push(jq, value);+ }+ break;+ }++ do_backtrack:+ case BACKTRACK: {+ pc = stack_restore(jq);+ if (!pc) {+ if (!jv_is_valid(jq->error)) {+ jv error = jq->error;+ jq->error = jv_null();+ return error;+ }+ return jv_invalid();+ }+ backtracking = 1;+ break;+ }++ case FORK_OPT:+ case FORK: {+ stack_save(jq, pc - 1, stack_get_pos(jq));+ pc++; // skip offset this time+ break;+ }++ case ON_BACKTRACK(FORK_OPT): {+ if (jv_is_valid(jq->error)) {+ // `try EXP ...` backtracked here (no value, `empty`), so we backtrack more+ jv_free(stack_pop(jq));+ goto do_backtrack;+ }+ // `try EXP ...` exception caught in EXP+ jv_free(stack_pop(jq)); // free the input+ stack_push(jq, jv_invalid_get_msg(jq->error)); // push the error's message+ jq->error = jv_null();+ uint16_t offset = *pc++;+ pc += offset;+ break;+ }+ case ON_BACKTRACK(FORK): {+ if (raising) goto do_backtrack;+ uint16_t offset = *pc++;+ pc += offset;+ break;+ }++ case CALL_BUILTIN: {+ int nargs = *pc++;+ jv top = stack_pop(jq);+ jv* in = cfunc_input;+ in[0] = top;+ for (int i = 1; i < nargs; i++) {+ in[i] = stack_pop(jq);+ }+ struct cfunction* function = &frame_current(jq)->bc->globals->cfunctions[*pc++];+ typedef jv (*func_1)(jq_state*,jv);+ typedef jv (*func_2)(jq_state*,jv,jv);+ typedef jv (*func_3)(jq_state*,jv,jv,jv);+ typedef jv (*func_4)(jq_state*,jv,jv,jv,jv);+ typedef jv (*func_5)(jq_state*,jv,jv,jv,jv,jv);+ switch (function->nargs) {+ case 1: top = ((func_1)function->fptr)(jq, in[0]); break;+ case 2: top = ((func_2)function->fptr)(jq, in[0], in[1]); break;+ case 3: top = ((func_3)function->fptr)(jq, in[0], in[1], in[2]); break;+ case 4: top = ((func_4)function->fptr)(jq, in[0], in[1], in[2], in[3]); break;+ case 5: top = ((func_5)function->fptr)(jq, in[0], in[1], in[2], in[3], in[4]); break;+ // FIXME: a) up to 7 arguments (input + 6), b) should assert+ // because the compiler should not generate this error.+ default: return jv_invalid_with_msg(jv_string("Function takes too many arguments"));+ }++ if (jv_is_valid(top)) {+ stack_push(jq, top);+ } else if (jv_invalid_has_msg(jv_copy(top))) {+ set_error(jq, top);+ goto do_backtrack;+ } else {+ // C-coded function returns invalid w/o msg? -> backtrack, as if+ // it had returned `empty`+ goto do_backtrack;+ }+ break;+ }++ case TAIL_CALL_JQ:+ case CALL_JQ: {+ /*+ * Bytecode layout here:+ *+ * CALL_JQ+ * <nclosures> (i.e., number of call arguments)+ * <callee closure> (what we're calling)+ * <nclosures' worth of closures> (frame reference + code pointer)+ *+ * <next instruction (to return to)>+ *+ * Each closure consists of two uint16_t values: a "level"+ * identifying the frame to be closed over, and an index.+ *+ * The level is a relative number of call frames reachable from+ * the currently one; 0 -> current frame, 1 -> previous frame, and+ * so on.+ *+ * The index is either an index of the closed frame's subfunctions+ * or of the closed frame's parameter closures. If the latter,+ * that closure will be passed, else the closed frame's pointer+ * and the subfunction's code will form the closure to be passed.+ *+ * See make_closure() for more information.+ */+ jv input = stack_pop(jq);+ uint16_t nclosures = *pc++;+ uint16_t* retaddr = pc + 2 + nclosures*2;+ stack_ptr retdata = jq->stk_top;+ struct frame* new_frame;+ struct closure cl = make_closure(jq, pc);+ if (opcode == TAIL_CALL_JQ) {+ retaddr = frame_current(jq)->retaddr;+ retdata = frame_current(jq)->retdata;+ frame_pop(jq);+ }+ new_frame = frame_push(jq, cl, pc + 2, nclosures);+ new_frame->retdata = retdata;+ new_frame->retaddr = retaddr;+ pc = new_frame->bc->code;+ stack_push(jq, input);+ break;+ }++ case RET: {+ jv value = stack_pop(jq);+ assert(jq->stk_top == frame_current(jq)->retdata);+ uint16_t* retaddr = frame_current(jq)->retaddr;+ if (retaddr) {+ // function return+ pc = retaddr;+ frame_pop(jq);+ } else {+ // top-level return, yielding value+ struct stack_pos spos = stack_get_pos(jq);+ stack_push(jq, jv_null());+ stack_save(jq, pc - 1, spos);+ return value;+ }+ stack_push(jq, value);+ break;+ }+ case ON_BACKTRACK(RET): {+ // resumed after top-level return+ goto do_backtrack;+ }+ }+ }+}++jv jq_format_error(jv msg) {+ if (jv_get_kind(msg) == JV_KIND_NULL ||+ (jv_get_kind(msg) == JV_KIND_INVALID && !jv_invalid_has_msg(jv_copy(msg)))) {+ jv_free(msg);+ fprintf(stderr, "jq: error: out of memory\n");+ return jv_null();+ }++ if (jv_get_kind(msg) == JV_KIND_STRING)+ return msg; // expected to already be formatted++ if (jv_get_kind(msg) == JV_KIND_INVALID)+ msg = jv_invalid_get_msg(msg);++ if (jv_get_kind(msg) == JV_KIND_NULL)+ return jq_format_error(msg); // ENOMEM++ // Invalid with msg; prefix with "jq: error: "++ if (jv_get_kind(msg) != JV_KIND_INVALID) {+ if (jv_get_kind(msg) == JV_KIND_STRING)+ return jv_string_fmt("jq: error: %s", jv_string_value(msg));++ msg = jv_dump_string(msg, JV_PRINT_INVALID);+ if (jv_get_kind(msg) == JV_KIND_STRING)+ return jv_string_fmt("jq: error: %s", jv_string_value(msg));+ return jq_format_error(jv_null()); // ENOMEM+ }++ // An invalid inside an invalid!+ return jq_format_error(jv_invalid_get_msg(msg));+}++// XXX Refactor into a utility function that returns a jv and one that+// uses it and then prints that jv's string as the complete error+// message.+static void default_err_cb(void *data, jv msg) {+ msg = jq_format_error(msg);+ fprintf((FILE *)data, "%s\n", jv_string_value(msg));+ jv_free(msg);+}++jq_state *jq_init(void) {+ jq_state *jq;+ jq = jv_mem_alloc_unguarded(sizeof(*jq));+ if (jq == NULL)+ return NULL;++ jq->bc = 0;+ jq->next_label = 0;++ stack_init(&jq->stk);+ jq->stk_top = 0;+ jq->fork_top = 0;+ jq->curr_frame = 0;+ jq->error = jv_null();++ jq->err_cb = default_err_cb;+ jq->err_cb_data = stderr;++ jq->attrs = jv_object();+ jq->path = jv_null();+ jq->value_at_path = jv_null();+ return jq;+}++void jq_set_error_cb(jq_state *jq, jq_msg_cb cb, void *data) {+ if (cb == NULL) {+ jq->err_cb = default_err_cb;+ jq->err_cb_data = stderr;+ } else {+ jq->err_cb = cb;+ jq->err_cb_data = data;+ }+}++void jq_get_error_cb(jq_state *jq, jq_msg_cb *cb, void **data) {+ *cb = jq->err_cb;+ *data = jq->err_cb_data;+}++void jq_set_nomem_handler(jq_state *jq, void (*nomem_handler)(void *), void *data) {+ jv_nomem_handler(nomem_handler, data);+ jq->nomem_handler = nomem_handler;+ jq->nomem_handler_data = data;+}+++void jq_start(jq_state *jq, jv input, int flags) {+ jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);+ jq_reset(jq);++ struct closure top = {jq->bc, -1};+ struct frame* top_frame = frame_push(jq, top, 0, 0);+ top_frame->retdata = 0;+ top_frame->retaddr = 0;++ stack_push(jq, input);+ stack_save(jq, jq->bc->code, stack_get_pos(jq));+ if (flags & JQ_DEBUG_TRACE) {+ jq->debug_trace_enabled = 1;+ } else {+ jq->debug_trace_enabled = 0;+ }+ jq->initial_execution = 1;+}++void jq_teardown(jq_state **jq) {+ jq_state *old_jq = *jq;+ if (old_jq == NULL)+ return;+ *jq = NULL;++ jq_reset(old_jq);+ bytecode_free(old_jq->bc);+ old_jq->bc = 0;+ jv_free(old_jq->attrs);++ jv_mem_free(old_jq);+}++static int ret_follows(uint16_t *pc) {+ if (*pc == RET)+ return 1;+ if (*pc++ != JUMP)+ return 0;+ return ret_follows(pc + *pc + 1); // FIXME, might be ironic+}++/*+ * Look for tail calls that can be optimized: tail calls with no+ * references left to the current frame.+ *+ * We're staring at this bytecode layout:+ *+ * CALL_JQ+ * <nclosures>+ * <callee closure> (2 units)+ * <nclosures closures> (2 units each)+ * <next instruction>+ *+ * A closure is:+ *+ * <level> (a relative frame count chased via the current frame's env)+ * <index> (an index of a subfunction or closure in that frame)+ *+ * We're looking for:+ *+ * a) the next instruction is a RET or a chain of unconditional JUMPs+ * that ends in a RET, and+ *+ * b) none of the closures -callee included- have level == 0.+ */+static uint16_t tail_call_analyze(uint16_t *pc) {+ assert(*pc == CALL_JQ);+ pc++;+ // + 1 for the callee closure+ for (uint16_t nclosures = *pc++ + 1; nclosures > 0; pc++, nclosures--) {+ if (*pc++ == 0)+ return CALL_JQ;+ }+ if (ret_follows(pc))+ return TAIL_CALL_JQ;+ return CALL_JQ;+}++static struct bytecode *optimize_code(struct bytecode *bc) {+ uint16_t *pc = bc->code;+ // FIXME: Don't mutate bc->code...+ while (pc < bc->code + bc->codelen) {+ switch (*pc) {+ case CALL_JQ:+ *pc = tail_call_analyze(pc);+ break;++ // Other bytecode optimizations here. A peephole optimizer would+ // fit right in.+ default: break;+ }+ pc += bytecode_operation_length(pc);+ }+ return bc;+}++static struct bytecode *optimize(struct bytecode *bc) {+ for (int i=0; i<bc->nsubfunctions; i++) {+ bc->subfunctions[i] = optimize(bc->subfunctions[i]);+ }+ return optimize_code(bc);+}++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;+ locations = locfile_init(jq, "<top-level>", str, strlen(str));+ block program;+ jq_reset(jq);+ if (jq->bc) {+ bytecode_free(jq->bc);+ jq->bc = 0;+ }+ int nerrors = load_program(jq, locations, &program);+ if (nerrors == 0) {+ jv_array_foreach(args, i, arg) {+ 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, &jq->bc, locations);+ }+ }+ if (nerrors)+ jq_report_error(jq, jv_string_fmt("jq: %d compile %s", nerrors, nerrors > 1 ? "errors" : "error"));+ if (jq->bc)+ jq->bc = optimize(jq->bc);+ jv_free(args);+ locfile_free(locations);+ return jq->bc != NULL;+}++int jq_compile(jq_state *jq, const char* str) {+ return jq_compile_args(jq, str, jv_array());+}++jv jq_get_jq_origin(jq_state *jq) {+ return jq_get_attr(jq, jv_string("JQ_ORIGIN"));+}++jv jq_get_prog_origin(jq_state *jq) {+ return jq_get_attr(jq, jv_string("PROGRAM_ORIGIN"));+}++jv jq_get_lib_dirs(jq_state *jq) {+ return jq_get_attr(jq, jv_string("JQ_LIBRARY_PATH"));+}++void jq_set_attrs(jq_state *jq, jv attrs) {+ assert(jv_get_kind(attrs) == JV_KIND_OBJECT);+ jv_free(jq->attrs);+ jq->attrs = attrs;+}++void jq_set_attr(jq_state *jq, jv attr, jv val) {+ jq->attrs = jv_object_set(jq->attrs, attr, val);+}++jv jq_get_attr(jq_state *jq, jv attr) {+ return jv_object_get(jv_copy(jq->attrs), attr);+}++void jq_dump_disassembly(jq_state *jq, int indent) {+ dump_disassembly(indent, jq->bc);+}++void jq_set_input_cb(jq_state *jq, jq_input_cb cb, void *data) {+ jq->input_cb = cb;+ jq->input_cb_data = data;+}++void jq_get_input_cb(jq_state *jq, jq_input_cb *cb, void **data) {+ *cb = jq->input_cb;+ *data = jq->input_cb_data;+}++void jq_set_debug_cb(jq_state *jq, jq_msg_cb cb, void *data) {+ jq->debug_cb = cb;+ jq->debug_cb_data = data;+}++void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) {+ *cb = jq->debug_cb;+ *data = jq->debug_cb_data;+}
@@ -0,0 +1,112 @@++#define _GNU_SOURCE /* for RTLD_NEXT */+#include <assert.h>+#include <dlfcn.h>+#include <errno.h>+#include <stdio.h>+#include <string.h>+#include <unistd.h>++static FILE *fail;+static FILE *fail_read;+static FILE *fail_write;+static FILE *fail_close;+static int error;++static FILE * (*real_fopen)(const char *, const char *);+static int (*real_fclose)(FILE *);+static int (*real_ferror)(FILE *);+static void (*real_clearerr)(FILE *);+static char * (*real_fgets)(char *, int, FILE *);+static size_t (*real_fread)(void *, size_t, size_t, FILE *);+static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *);++#define GET_REAL(sym) \+ do { \+ if (real_ ## sym == 0) { \+ real_ ## sym = dlsym(RTLD_NEXT, #sym); \+ assert(real_ ## sym != 0); \+ } \+ } while (0)++#define dbg_write(msg) (void)write(2, msg, sizeof(msg) - 1)++#define dbg() \+ do { \+ dbg_write("here: "); \+ dbg_write(__func__); \+ dbg_write("!\n"); \+ } while (0)++FILE *fopen(const char *path, const char *mode) {+ GET_REAL(fopen);+ fail = fail_read = fail_write = fail_close = 0;+ FILE *f = real_fopen(path, mode);+ error = EIO;+ if (strcmp(path, "fail_read") == 0) {+ fail = fail_read = f;+ } else if (strncmp(path, "fail_write", sizeof("fail_write") - 1) == 0) {+ // Not that jq opens files for write anyways...+ fail = fail_write = f;+ if (strcmp(path, "fail_write_enospc") == 0)+ error = ENOSPC;+ } else if (strncmp(path, "fail_close", sizeof("fail_close") - 1) == 0) {+ fail = fail_close = f;+ if (strcmp(path, "fail_close_enospc") == 0)+ error = ENOSPC;+ }+ return f;+}++int fclose(FILE *f) {+ GET_REAL(fclose);+ int res = real_fclose(f);+ if (fail_close == f) {+ fail = fail_read = fail_write = fail_close = 0;+ return EOF;+ }+ return res;+}++char * fgets(char *buf, int len, FILE *f) {+ GET_REAL(fgets);+ char *res = real_fgets(buf, len, f);+ if (fail_read == f)+ return 0;+ return res;+}++size_t fread(void *buf, size_t sz, size_t nemb, FILE *f) {+ GET_REAL(fread);+ size_t res = real_fread(buf, sz, nemb, f);+ if (fail_read == f)+ return 0;+ return res;+}++size_t fwrite(const void *buf, size_t sz, size_t nemb, FILE *f) {+ GET_REAL(fwrite);+ size_t res = real_fwrite(buf, sz, nemb, f);+ if (fail_write == f)+ return 0;+ return res;+}++int ferror(FILE *f) {+ GET_REAL(ferror);+ int res = real_ferror(f);+ if (fail == f) {+ errno = error;+ return 1;+ }+ return res;+}++void clearerr(FILE *f) {+ GET_REAL(clearerr);+ real_clearerr(f);+ if (fail == f) {+ fail = fail_read = fail_write = fail_close = 0;+ error = 0;+ }+}
@@ -0,0 +1,60 @@+#ifndef JQ_H+#define JQ_H++#include <stdio.h>+#include "jv.h"++enum {JQ_DEBUG_TRACE = 1};++typedef struct jq_state jq_state;+typedef void (*jq_msg_cb)(void *, jv);++jq_state *jq_init(void);+void jq_set_error_cb(jq_state *, jq_msg_cb, void *);+void jq_get_error_cb(jq_state *, jq_msg_cb *, void **);+void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);+jv jq_format_error(jv msg);+void jq_report_error(jq_state *, jv);+int jq_compile(jq_state *, const char*);+int jq_compile_args(jq_state *, const char*, jv);+void jq_dump_disassembly(jq_state *, int);+void jq_start(jq_state *, jv value, int);+jv jq_next(jq_state *);+void jq_teardown(jq_state **);++typedef jv (*jq_input_cb)(jq_state *, void *);+void jq_set_input_cb(jq_state *, jq_input_cb, void *);+void jq_get_input_cb(jq_state *, jq_input_cb *, void **);++void jq_set_debug_cb(jq_state *, jq_msg_cb, void *);+void jq_get_debug_cb(jq_state *, jq_msg_cb *, void **);++void jq_set_attrs(jq_state *, jv);+jv jq_get_attrs(jq_state *);+jv jq_get_jq_origin(jq_state *);+jv jq_get_prog_origin(jq_state *);+jv jq_get_lib_dirs(jq_state *);+void jq_set_attr(jq_state *, jv, jv);+jv jq_get_attr(jq_state *, jv);++/*+ * We use char * instead of jf for filenames here because filenames+ * should be in the process' locale's codeset, which may not be UTF-8,+ * whereas jv string values must be in UTF-8. This way the caller+ * doesn't have to perform any codeset conversions.+ */+typedef struct jq_util_input_state jq_util_input_state;+typedef void (*jq_util_msg_cb)(void *, const char *);++jq_util_input_state *jq_util_input_init(jq_util_msg_cb, void *);+void jq_util_input_set_parser(jq_util_input_state *, jv_parser *, int);+void jq_util_input_free(jq_util_input_state **);+void jq_util_input_add_input(jq_util_input_state *, const char *);+int jq_util_input_errors(jq_util_input_state *);+jv jq_util_input_next_input(jq_util_input_state *);+jv jq_util_input_next_input_cb(jq_state *, void *);+jv jq_util_input_get_position(jq_state*);+jv jq_util_input_get_current_filename(jq_state*);+jv jq_util_input_get_current_line(jq_state*);++#endif /* !JQ_H */
@@ -0,0 +1,9 @@+#ifndef JQ_PARSER_H+#define JQ_PARSER_H+#include "locfile.h"+#include "compile.h"++int jq_parse(struct locfile* source, block* answer);+int jq_parse_library(struct locfile* locations, block* answer);++#endif
@@ -0,0 +1,346 @@+#include <assert.h>+#include <stdio.h>+#include <string.h>+#include <stdlib.h>+#include "jv.h"+#include "jq.h"++static void jv_test();+static void run_jq_tests(jv, int, FILE *);+++int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) {+ FILE *testdata = stdin;+ jv_test();+ if (argc > 0) {+ testdata = fopen(argv[0], "r");+ if (!testdata) {+ perror("fopen");+ exit(1);+ }+ }+ run_jq_tests(libdirs, verbose, testdata);+ return 0;+}++static int skipline(const char* buf) {+ int p = 0;+ while (buf[p] == ' ' || buf[p] == '\t') p++;+ if (buf[p] == '#' || buf[p] == '\n' || buf[p] == 0) return 1;+ return 0;+}++static int checkerrormsg(const char* buf) {+ return strcmp(buf, "%%FAIL\n") == 0;+}++static int checkfail(const char* buf) {+ return strcmp(buf, "%%FAIL\n") == 0 || strcmp(buf, "%%FAIL IGNORE MSG\n") == 0;+}++struct err_data {+ char buf[4096];+};++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);+}++static void run_jq_tests(jv lib_dirs, int verbose, 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);+ 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);+ must_fail = 0;+ check_msg = 0;+ 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++;+ }+ must_fail = 0;+ check_msg = 0;+ 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;+ }+ if (verbose) {+ 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)) {+ printf("*** Input is invalid on line %u: %s\n", lineno, buf);+ invalid++;+ continue;+ }+ jq_start(jq, input, verbose ? JQ_DEBUG_TRACE : 0);++ while (fgets(buf, sizeof(buf), testdata)) {+ lineno++;+ if (skipline(buf)) break;+ jv expected = jv_parse(buf);+ if (!jv_is_valid(expected)) {+ printf("*** Expected result is invalid on line %u: %s\n", lineno, buf);+ 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_COLOR|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);+ }+}
@@ -0,0 +1,1333 @@+#include <stdint.h>+#include <stddef.h>+#include <assert.h>+#include <stdlib.h>+#include <stdio.h>+#include <string.h>+#include <stdarg.h>+#include <limits.h>++#include "jv_alloc.h"+#include "jv.h"+#include "jv_unicode.h"+#include "util.h"++/*+ * Internal refcounting helpers+ */++typedef struct jv_refcnt {+ int count;+} jv_refcnt;++static const jv_refcnt JV_REFCNT_INIT = {1};++static void jvp_refcnt_inc(jv_refcnt* c) {+ c->count++;+}++static int jvp_refcnt_dec(jv_refcnt* c) {+ c->count--;+ return c->count == 0;+}++static int jvp_refcnt_unshared(jv_refcnt* c) {+ assert(c->count > 0);+ return c->count == 1;+}++/*+ * Simple values (true, false, null)+ */++#define KIND_MASK 0xf++jv_kind jv_get_kind(jv x) {+ return x.kind_flags & KIND_MASK;+}++const char* jv_kind_name(jv_kind k) {+ switch (k) {+ case JV_KIND_INVALID: return "<invalid>";+ case JV_KIND_NULL: return "null";+ case JV_KIND_FALSE: return "boolean";+ case JV_KIND_TRUE: return "boolean";+ case JV_KIND_NUMBER: return "number";+ case JV_KIND_STRING: return "string";+ case JV_KIND_ARRAY: return "array";+ case JV_KIND_OBJECT: return "object";+ }+ assert(0 && "invalid kind");+ 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}};++jv jv_true() {+ return JV_TRUE;+}++jv jv_false() {+ return JV_FALSE;+}++jv jv_null() {+ return JV_NULL;+}++jv jv_bool(int x) {+ return x ? JV_TRUE : JV_FALSE;+}++/*+ * Invalid objects, with optional error messages+ */++typedef struct {+ jv_refcnt refcnt;+ jv errmsg;+} jvp_invalid;++jv jv_invalid_with_msg(jv err) {+ if (jv_get_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}};+ return x;+}++jv jv_invalid() {+ return JV_INVALID;+}++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;+}++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);+ 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)) {+ jv_free(((jvp_invalid*)x.u.ptr)->errmsg);+ jv_mem_free(x.u.ptr);+ }+}++/*+ * Numbers+ */++jv jv_number(double x) {+ jv j = {JV_KIND_NUMBER, 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;+}++int jv_is_integer(jv j){+ if(jv_get_kind(j) != JV_KIND_NUMBER){+ return 0;+ }+ double x = jv_number_value(j);+ if(x != x || x > INT_MAX || x < INT_MIN){+ return 0;+ }++ return x == (int)x;+}++/*+ * Arrays (internal helpers)+ */++#define ARRAY_SIZE_ROUND_UP(n) (((n)*3)/2)++static int imax(int a, int b) {+ if (a>b) return a;+ else return b;+}++//FIXME signed vs unsigned+typedef struct {+ jv_refcnt refcnt;+ int length, alloc_length;+ jv elements[];+} jvp_array;++static jvp_array* jvp_array_ptr(jv a) {+ assert(jv_get_kind(a) == JV_KIND_ARRAY);+ return (jvp_array*)a.u.ptr;+}++static jvp_array* jvp_array_alloc(unsigned size) {+ jvp_array* a = jv_mem_alloc(sizeof(jvp_array) + sizeof(jv) * size);+ a->refcnt.count = 1;+ a->length = 0;+ a->alloc_length = size;+ return a;+}++static jv jvp_array_new(unsigned size) {+ jv r = {JV_KIND_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);+ if (jvp_refcnt_dec(a.u.ptr)) {+ jvp_array* array = jvp_array_ptr(a);+ for (int i=0; i<array->length; i++) {+ jv_free(array->elements[i]);+ }+ jv_mem_free(array);+ }+}++static int jvp_array_length(jv a) {+ assert(jv_get_kind(a) == JV_KIND_ARRAY);+ return a.size;+}++static int jvp_array_offset(jv a) {+ assert(jv_get_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);+ if (i >= 0 && i < jvp_array_length(a)) {+ jvp_array* array = jvp_array_ptr(a);+ assert(i + jvp_array_offset(a) < array->length);+ return &array->elements[i + jvp_array_offset(a)];+ } else {+ return 0;+ }+}++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];+ }+}++static int jvp_array_equal(jv a, jv b) {+ if (jvp_array_length(a) != jvp_array_length(b))+ return 0;+ if (jvp_array_ptr(a) == jvp_array_ptr(b) &&+ jvp_array_offset(a) == jvp_array_offset(b))+ return 1;+ for (int i=0; i<jvp_array_length(a); i++) {+ if (!jv_equal(jv_copy(*jvp_array_read(a, i)),+ jv_copy(*jvp_array_read(b, i))))+ return 0;+ }+ return 1;+}++static void jvp_clamp_slice_params(int len, int *pstart, int *pend)+{+ if (*pstart < 0) *pstart = len + *pstart;+ if (*pend < 0) *pend = len + *pend;++ if (*pstart < 0) *pstart = 0;+ if (*pstart > len) *pstart = len;+ if (*pend > len) *pend = len;+ if (*pend < *pstart) *pend = *pstart;+}++static jv jvp_array_slice(jv a, int start, int end) {+ assert(jv_get_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);++ // FIXME: maybe slice should reallocate if the slice is small enough+ if (start == end) {+ jv_free(a);+ return jv_array();+ }+ // FIXME FIXME FIXME large offsets+ a.offset += start;+ a.size = end - start;+ return a;+}++/*+ * Arrays (public interface)+ */++jv jv_array_sized(int n) {+ return jvp_array_new(n);+}++jv jv_array() {+ return jv_array_sized(16);+}++int jv_array_length(jv j) {+ assert(jv_get_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);+ jv* slot = jvp_array_read(j, idx);+ jv val;+ if (slot) {+ val = jv_copy(*slot);+ } else {+ val = jv_invalid();+ }+ jv_free(j);+ return val;+}++jv jv_array_set(jv j, int idx, jv val) {+ assert(jv_get_kind(j) == JV_KIND_ARRAY);++ if (idx < 0)+ idx = jvp_array_length(j) + idx;+ if (idx < 0) {+ jv_free(j);+ jv_free(val);+ return jv_invalid_with_msg(jv_string("Out of bounds negative array index"));+ }+ // copy/free of val,j coalesced+ jv* slot = jvp_array_write(&j, idx);+ jv_free(*slot);+ *slot = val;+ return j;+}++jv jv_array_append(jv j, jv val) {+ // copy/free of val,j coalesced+ return jv_array_set(j, jv_array_length(jv_copy(j)), 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);++ // FIXME: could be faster+ jv_array_foreach(b, i, elem) {+ a = jv_array_append(a, elem);+ }+ jv_free(b);+ return a;+}++jv jv_array_slice(jv a, int start, int end) {+ assert(jv_get_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_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))))+ idx = -1;+ else if (bi == 0 && idx == -1)+ idx = ai;+ }+ if (idx > -1)+ res = jv_array_append(res, jv_number(idx));+ idx = -1;+ }+ jv_free(a);+ jv_free(b);+ return res;+}+++/*+ * Strings (internal helpers)+ */++typedef struct {+ jv_refcnt refcnt;+ uint32_t hash;+ // high 31 bits are length, low bit is a flag+ // indicating whether hash has been computed.+ uint32_t length_hashed;+ uint32_t alloc_length;+ char data[];+} jvp_string;++static jvp_string* jvp_string_ptr(jv a) {+ assert(jv_get_kind(a) == JV_KIND_STRING);+ return (jvp_string*)a.u.ptr;+}++static jvp_string* jvp_string_alloc(uint32_t size) {+ jvp_string* s = jv_mem_alloc(sizeof(jvp_string) + size + 1);+ s->refcnt.count = 1;+ s->alloc_length = size;+ return s;+}++/* Copy a UTF8 string, replacing all badly encoded points with U+FFFD */+static jv jvp_string_copy_replace_bad(const char* data, uint32_t length) {+ const char* end = data + length;+ const char* i = data;+ const char* cstart;++ uint32_t maxlength = length * 3 + 1; // worst case: all bad bytes, each becomes a 3-byte U+FFFD+ jvp_string* s = jvp_string_alloc(maxlength);+ char* out = s->data;+ int c = 0;++ while ((i = jvp_utf8_next((cstart = i), end, &c))) {+ if (c == -1) {+ c = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER+ }+ out += jvp_utf8_encode(c, out);+ assert(out < s->data + maxlength);+ }+ length = out - s->data;+ s->data[length] = 0;+ s->length_hashed = length << 1;+ jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}};+ return r;+}++/* Assumes valid UTF8 */+static jv jvp_string_new(const char* data, uint32_t length) {+ jvp_string* s = jvp_string_alloc(length);+ s->length_hashed = length << 1;+ if (data != NULL)+ memcpy(s->data, data, length);+ s->data[length] = 0;+ jv r = {JV_KIND_STRING, 0, 0, 0, {&s->refcnt}};+ return r;+}++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}};+ return r;+}+++static void jvp_string_free(jv js) {+ jvp_string* s = jvp_string_ptr(js);+ if (jvp_refcnt_dec(&s->refcnt)) {+ jv_mem_free(s);+ }+}++static uint32_t jvp_string_length(jvp_string* s) {+ return s->length_hashed >> 1;+}++static uint32_t jvp_string_remaining_space(jvp_string* s) {+ assert(s->alloc_length >= jvp_string_length(s));+ uint32_t r = s->alloc_length - jvp_string_length(s);+ return r;+}++static jv jvp_string_append(jv string, const char* data, uint32_t len) {+ jvp_string* s = jvp_string_ptr(string);+ uint32_t currlen = jvp_string_length(s);++ if (jvp_refcnt_unshared(string.u.ptr) &&+ jvp_string_remaining_space(s) >= len) {+ // the next string fits at the end of a+ memcpy(s->data + currlen, data, len);+ s->data[currlen + len] = 0;+ s->length_hashed = (currlen + len) << 1;+ return string;+ } else {+ // allocate a bigger buffer and copy+ uint32_t allocsz = (currlen + len) * 2;+ if (allocsz < 32) allocsz = 32;+ jvp_string* news = jvp_string_alloc(allocsz);+ news->length_hashed = (currlen + len) << 1;+ memcpy(news->data, s->data, currlen);+ 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}};+ return r;+ }+}++static const uint32_t HASH_SEED = 0x432A9843;++static uint32_t rotl32 (uint32_t x, int8_t r){+ return (x << r) | (x >> (32 - r));+}++static uint32_t jvp_string_hash(jv jstr) {+ jvp_string* str = jvp_string_ptr(jstr);+ if (str->length_hashed & 1)+ return str->hash;++ /* The following is based on MurmurHash3.+ MurmurHash3 was written by Austin Appleby, and is placed+ in the public domain. */++ const uint8_t* data = (const uint8_t*)str->data;+ int len = (int)jvp_string_length(str);+ const int nblocks = len / 4;++ uint32_t h1 = HASH_SEED;++ const uint32_t c1 = 0xcc9e2d51;+ const uint32_t c2 = 0x1b873593;+ const uint32_t* blocks = (const uint32_t *)(data + nblocks*4);++ for(int i = -nblocks; i; i++) {+ uint32_t k1 = blocks[i]; //FIXME: endianness/alignment++ k1 *= c1;+ k1 = rotl32(k1,15);+ k1 *= c2;++ h1 ^= k1;+ h1 = rotl32(h1,13);+ h1 = h1*5+0xe6546b64;+ }++ const uint8_t* tail = (const uint8_t*)(data + nblocks*4);++ uint32_t k1 = 0;++ switch(len & 3) {+ case 3: k1 ^= tail[2] << 16;+ case 2: k1 ^= tail[1] << 8;+ case 1: k1 ^= tail[0];+ k1 *= c1; k1 = rotl32(k1,15); k1 *= c2; h1 ^= k1;+ }++ h1 ^= len;++ h1 ^= h1 >> 16;+ h1 *= 0x85ebca6b;+ h1 ^= h1 >> 13;+ h1 *= 0xc2b2ae35;+ h1 ^= h1 >> 16;++ str->length_hashed |= 1;+ str->hash = h1;++ 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);+ 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;+ return memcmp(stra->data, strb->data, jvp_string_length(stra)) == 0;+}++/*+ * Strings (public API)+ */++jv jv_string_sized(const char* str, int len) {+ return+ jvp_utf8_is_valid(str, str+len) ?+ jvp_string_new(str, len) :+ jvp_string_copy_replace_bad(str, len);+}++jv jv_string_empty(int len) {+ return jvp_string_empty_new(len);+}++jv jv_string(const char* str) {+ return jv_string_sized(str, strlen(str));+}++int jv_string_length_bytes(jv j) {+ assert(jv_get_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);+ const char* i = jv_string_value(j);+ const char* end = i + jv_string_length_bytes(jv_copy(j));+ int c = 0, len = 0;+ while ((i = jvp_utf8_next(i, end, &c))) len++;+ jv_free(j);+ return len;+}+++jv jv_string_indexes(jv j, jv k) {+ assert(jv_get_kind(j) == JV_KIND_STRING);+ assert(jv_get_kind(k) == JV_KIND_STRING);+ const char *jstr = jv_string_value(j);+ const char *idxstr = jv_string_value(k);+ const char *p;+ int jlen = jv_string_length_bytes(jv_copy(j));+ int idxlen = jv_string_length_bytes(jv_copy(k));+ jv a = jv_array();++ p = jstr;+ while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) {+ a = jv_array_append(a, jv_number(p - jstr));+ p += idxlen;+ }+ jv_free(j);+ jv_free(k);+ return a;+}++jv jv_string_split(jv j, jv sep) {+ assert(jv_get_kind(j) == JV_KIND_STRING);+ assert(jv_get_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);+ const char *p, *s;+ int seplen = jv_string_length_bytes(jv_copy(sep));+ jv a = jv_array();++ assert(jv_get_refcnt(a) == 1);++ if (seplen == 0) {+ int c;+ while ((jstr = jvp_utf8_next(jstr, jend, &c)))+ a = jv_array_append(a, jv_string_append_codepoint(jv_string(""), c));+ } else {+ for (p = jstr; p < jend; p = s + seplen) {+ s = _jq_memmem(p, jend - p, sepstr, seplen);+ if (s == NULL)+ s = jend;+ a = jv_array_append(a, jv_string_sized(p, s - p));+ // Add an empty string to denote that j ends on a sep+ if (s + seplen == jend && seplen != 0)+ a = jv_array_append(a, jv_string(""));+ }+ }+ jv_free(j);+ jv_free(sep);+ return a;+}++jv jv_string_explode(jv j) {+ assert(jv_get_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;+ jv a = jv_array_sized(len);+ int c;+ while ((i = jvp_utf8_next(i, end, &c)))+ a = jv_array_append(a, jv_number(c));+ jv_free(j);+ return a;+}++jv jv_string_implode(jv j) {+ assert(jv_get_kind(j) == JV_KIND_ARRAY);+ int len = jv_array_length(jv_copy(j));+ jv s = jv_string_empty(len);+ int i;++ assert(len >= 0);++ for (i = 0; i < len; i++) {+ jv n = jv_array_get(jv_copy(j), i);+ assert(jv_get_kind(n) == JV_KIND_NUMBER);+ int nv = jv_number_value(n);+ if (nv > 0x10FFFF)+ nv = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER+ s = jv_string_append_codepoint(s, nv);+ }++ jv_free(j);+ return s;+}++unsigned long jv_string_hash(jv j) {+ assert(jv_get_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);+ return jvp_string_ptr(j)->data;+}++jv jv_string_slice(jv j, int start, int end) {+ assert(jv_get_kind(j) == JV_KIND_STRING);+ const char *s = jv_string_value(j);+ int len = jv_string_length_bytes(jv_copy(j));+ int i;+ const char *p, *e;+ int c;+ jv res;++ jvp_clamp_slice_params(len, &start, &end);+ assert(0 <= start && start <= end && end <= len);++ /* Look for byte offset corresponding to start codepoints */+ for (p = s, i = 0; i < start; i++) {+ p = jvp_utf8_next(p, s + len, &c);+ if (p == NULL) {+ jv_free(j);+ return jv_string_empty(16);+ }+ if (c == -1) {+ jv_free(j);+ return jv_invalid_with_msg(jv_string("Invalid UTF-8 string"));+ }+ }+ /* Look for byte offset corresponding to end codepoints */+ for (e = p; e != NULL && i < end; i++) {+ e = jvp_utf8_next(e, s + len, &c);+ if (e == NULL) {+ e = s + len;+ break;+ }+ if (c == -1) {+ jv_free(j);+ return jv_invalid_with_msg(jv_string("Invalid UTF-8 string"));+ }+ }++ /*+ * NOTE: Ideally we should do here what jvp_array_slice() does instead+ * of allocating a new string as we do! However, we assume NUL-+ * terminated strings all over, and in the jv API, so for now we waste+ * memory like a drunken navy programmer. There's probably nothing we+ * can do about it.+ */+ res = jv_string_sized(p, e - p);+ jv_free(j);+ return res;+}++jv jv_string_concat(jv a, jv b) {+ a = jvp_string_append(a, jv_string_value(b),+ jvp_string_length(jvp_string_ptr(b)));+ jv_free(b);+ return a;+}++jv jv_string_append_buf(jv a, const char* buf, int len) {+ if (jvp_utf8_is_valid(buf, buf+len)) {+ a = jvp_string_append(a, buf, len);+ } else {+ jv b = jvp_string_copy_replace_bad(buf, len);+ a = jv_string_concat(a, b);+ }+ return a;+}++jv jv_string_append_codepoint(jv a, uint32_t c) {+ char buf[5];+ int len = jvp_utf8_encode(c, buf);+ a = jvp_string_append(a, buf, len);+ return a;+}++jv jv_string_append_str(jv a, const char* str) {+ return jv_string_append_buf(a, str, strlen(str));+}++jv jv_string_vfmt(const char* fmt, va_list ap) {+ int size = 1024;+ while (1) {+ char* buf = jv_mem_alloc(size);+ va_list ap2;+ va_copy(ap2, ap);+ int n = vsnprintf(buf, size, fmt, ap2);+ va_end(ap2);+ /*+ * NOTE: here we support old vsnprintf()s that return -1 because the+ * buffer is too small.+ */+ if (n >= 0 && n < size) {+ jv ret = jv_string_sized(buf, n);+ jv_mem_free(buf);+ return ret;+ } else {+ jv_mem_free(buf);+ size = (n > 0) ? /* standard */ (n * 2) : /* not standard */ (size * 2);+ }+ }+}++jv jv_string_fmt(const char* fmt, ...) {+ va_list args;+ va_start(args, fmt);+ jv res = jv_string_vfmt(fmt, args);+ va_end(args);+ return res;+}++/*+ * Objects (internal helpers)+ */++struct object_slot {+ int next; /* next slot with same hash, for collisions */+ uint32_t hash;+ jv string;+ jv value;+};++typedef struct {+ jv_refcnt refcnt;+ int next_free;+ struct object_slot elements[];+} jvp_object;+++/* warning: nontrivial justification of alignment */+static jv jvp_object_new(int size) {+ // Allocates an object of (size) slots and (size*2) hash buckets.++ // size must be a power of two+ assert(size > 0 && (size & (size - 1)) == 0);++ jvp_object* obj = jv_mem_alloc(sizeof(jvp_object) ++ sizeof(struct object_slot) * size ++ sizeof(int) * (size * 2));+ obj->refcnt.count = 1;+ for (int i=0; i<size; i++) {+ obj->elements[i].next = i - 1;+ obj->elements[i].string = JV_NULL;+ obj->elements[i].hash = 0;+ obj->elements[i].value = JV_NULL;+ }+ obj->next_free = 0;+ int* hashbuckets = (int*)(&obj->elements[size]);+ for (int i=0; i<size*2; i++) {+ hashbuckets[i] = -1;+ }+ jv r = {JV_KIND_OBJECT, 0, 0, size, {&obj->refcnt}};+ return r;+}++static jvp_object* jvp_object_ptr(jv o) {+ assert(jv_get_kind(o) == JV_KIND_OBJECT);+ return (jvp_object*)o.u.ptr;+}++static uint32_t jvp_object_mask(jv o) {+ assert(jv_get_kind(o) == JV_KIND_OBJECT);+ return (o.size * 2) - 1;+}++static int jvp_object_size(jv o) {+ assert(jv_get_kind(o) == JV_KIND_OBJECT);+ return o.size;+}++static int* jvp_object_buckets(jv o) {+ return (int*)(&jvp_object_ptr(o)->elements[o.size]);+}++static int* jvp_object_find_bucket(jv object, jv key) {+ return jvp_object_buckets(object) + (jvp_object_mask(object) & jvp_string_hash(key));+}++static struct object_slot* jvp_object_get_slot(jv object, int slot) {+ assert(slot == -1 || (slot >= 0 && slot < jvp_object_size(object)));+ if (slot == -1) return 0;+ else return &jvp_object_ptr(object)->elements[slot];+}++static struct object_slot* jvp_object_next_slot(jv object, struct object_slot* slot) {+ return jvp_object_get_slot(object, slot->next);+}++static struct object_slot* jvp_object_find_slot(jv object, jv keystr, int* bucket) {+ uint32_t hash = jvp_string_hash(keystr);+ for (struct object_slot* curr = jvp_object_get_slot(object, *bucket);+ curr;+ curr = jvp_object_next_slot(object, curr)) {+ if (curr->hash == hash && jvp_string_equal(keystr, curr->string)) {+ return curr;+ }+ }+ return 0;+}++static struct object_slot* jvp_object_add_slot(jv object, jv key, int* bucket) {+ jvp_object* o = jvp_object_ptr(object);+ int newslot_idx = o->next_free;+ if (newslot_idx == jvp_object_size(object)) return 0;+ struct object_slot* newslot = jvp_object_get_slot(object, newslot_idx);+ o->next_free++;+ newslot->next = *bucket;+ *bucket = newslot_idx;+ newslot->hash = jvp_string_hash(key);+ newslot->string = key;+ return newslot;+}++static jv* jvp_object_read(jv object, jv key) {+ assert(jv_get_kind(key) == JV_KIND_STRING);+ int* bucket = jvp_object_find_bucket(object, key);+ struct object_slot* slot = jvp_object_find_slot(object, key, bucket);+ if (slot == 0) return 0;+ else return &slot->value;+}++static void jvp_object_free(jv o) {+ assert(jv_get_kind(o) == JV_KIND_OBJECT);+ if (jvp_refcnt_dec(o.u.ptr)) {+ for (int i=0; i<jvp_object_size(o); i++) {+ struct object_slot* slot = jvp_object_get_slot(o, i);+ if (jv_get_kind(slot->string) != JV_KIND_NULL) {+ jvp_string_free(slot->string);+ jv_free(slot->value);+ }+ }+ jv_mem_free(jvp_object_ptr(o));+ }+}++static jv jvp_object_rehash(jv object) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ assert(jvp_refcnt_unshared(object.u.ptr));+ int size = jvp_object_size(object);+ jv new_object = jvp_object_new(size * 2);+ for (int i=0; i<size; i++) {+ struct object_slot* slot = jvp_object_get_slot(object, i);+ if (jv_get_kind(slot->string) == JV_KIND_NULL) continue;+ int* new_bucket = jvp_object_find_bucket(new_object, slot->string);+ assert(!jvp_object_find_slot(new_object, slot->string, new_bucket));+ struct object_slot* new_slot = jvp_object_add_slot(new_object, slot->string, new_bucket);+ assert(new_slot);+ new_slot->value = slot->value;+ }+ // references are transported, just drop the old table+ jv_mem_free(jvp_object_ptr(object));+ return new_object;+}++static jv jvp_object_unshare(jv object) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ if (jvp_refcnt_unshared(object.u.ptr))+ return object;++ jv new_object = jvp_object_new(jvp_object_size(object));+ jvp_object_ptr(new_object)->next_free = jvp_object_ptr(object)->next_free;+ for (int i=0; i<jvp_object_size(new_object); i++) {+ struct object_slot* old_slot = jvp_object_get_slot(object, i);+ struct object_slot* new_slot = jvp_object_get_slot(new_object, i);+ *new_slot = *old_slot;+ if (jv_get_kind(old_slot->string) != JV_KIND_NULL) {+ new_slot->string = jv_copy(old_slot->string);+ new_slot->value = jv_copy(old_slot->value);+ }+ }++ int* old_buckets = jvp_object_buckets(object);+ int* new_buckets = jvp_object_buckets(new_object);+ memcpy(new_buckets, old_buckets, sizeof(int) * jvp_object_size(new_object)*2);++ jvp_object_free(object);+ assert(jvp_refcnt_unshared(new_object.u.ptr));+ return new_object;+}++static jv* jvp_object_write(jv* object, jv key) {+ *object = jvp_object_unshare(*object);+ int* bucket = jvp_object_find_bucket(*object, key);+ struct object_slot* slot = jvp_object_find_slot(*object, key, bucket);+ if (slot) {+ // already has the key+ jvp_string_free(key);+ return &slot->value;+ }+ slot = jvp_object_add_slot(*object, key, bucket);+ if (slot) {+ slot->value = jv_invalid();+ } else {+ *object = jvp_object_rehash(*object);+ bucket = jvp_object_find_bucket(*object, key);+ assert(!jvp_object_find_slot(*object, key, bucket));+ slot = jvp_object_add_slot(*object, key, bucket);+ assert(slot);+ slot->value = jv_invalid();+ }+ return &slot->value;+}++static int jvp_object_delete(jv* object, jv key) {+ assert(jv_get_kind(key) == JV_KIND_STRING);+ *object = jvp_object_unshare(*object);+ int* bucket = jvp_object_find_bucket(*object, key);+ int* prev_ptr = bucket;+ uint32_t hash = jvp_string_hash(key);+ for (struct object_slot* curr = jvp_object_get_slot(*object, *bucket);+ curr;+ curr = jvp_object_next_slot(*object, curr)) {+ if (hash == curr->hash && jvp_string_equal(key, curr->string)) {+ *prev_ptr = curr->next;+ jvp_string_free(curr->string);+ curr->string = JV_NULL;+ jv_free(curr->value);+ return 1;+ }+ prev_ptr = &curr->next;+ }+ return 0;+}++static int jvp_object_length(jv object) {+ int n = 0;+ for (int i=0; i<jvp_object_size(object); i++) {+ struct object_slot* slot = jvp_object_get_slot(object, i);+ if (jv_get_kind(slot->string) != JV_KIND_NULL) n++;+ }+ return n;+}++static int jvp_object_equal(jv o1, jv o2) {+ int len2 = jvp_object_length(o2);+ int len1 = 0;+ for (int i=0; i<jvp_object_size(o1); i++) {+ struct object_slot* slot = jvp_object_get_slot(o1, i);+ if (jv_get_kind(slot->string) == JV_KIND_NULL) continue;+ jv* slot2 = jvp_object_read(o2, slot->string);+ if (!slot2) return 0;+ // FIXME: do less refcounting here+ if (!jv_equal(jv_copy(slot->value), jv_copy(*slot2))) return 0;+ len1++;+ }+ return len1 == len2;+}++/*+ * Objects (public interface)+ */+#define DEFAULT_OBJECT_SIZE 8+jv jv_object() {+ return jvp_object_new(8);+}++jv jv_object_get(jv object, jv key) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ assert(jv_get_kind(key) == JV_KIND_STRING);+ jv* slot = jvp_object_read(object, key);+ jv val;+ if (slot) {+ val = jv_copy(*slot);+ } else {+ val = jv_invalid();+ }+ jv_free(object);+ jv_free(key);+ return val;+}++jv jv_object_set(jv object, jv key, jv value) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ assert(jv_get_kind(key) == JV_KIND_STRING);+ // copy/free of object, key, value coalesced+ jv* slot = jvp_object_write(&object, key);+ jv_free(*slot);+ *slot = value;+ return object;+}++jv jv_object_delete(jv object, jv key) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ assert(jv_get_kind(key) == JV_KIND_STRING);+ jvp_object_delete(&object, key);+ jv_free(key);+ return object;+}++int jv_object_length(jv object) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ int n = jvp_object_length(object);+ jv_free(object);+ return n;+}++jv jv_object_merge(jv a, jv b) {+ assert(jv_get_kind(a) == JV_KIND_OBJECT);+ jv_object_foreach(b, k, v) {+ a = jv_object_set(a, k, v);+ }+ jv_free(b);+ return a;+}++jv jv_object_merge_recursive(jv a, jv b) {+ assert(jv_get_kind(a) == JV_KIND_OBJECT);+ assert(jv_get_kind(b) == JV_KIND_OBJECT);++ jv_object_foreach(b, k, v) {+ jv elem = jv_object_get(jv_copy(a), jv_copy(k));+ if (jv_is_valid(elem) &&+ jv_get_kind(elem) == JV_KIND_OBJECT &&+ jv_get_kind(v) == JV_KIND_OBJECT) {+ a = jv_object_set(a, k, jv_object_merge_recursive(elem, v));+ } else {+ jv_free(elem);+ a = jv_object_set(a, k, v);+ }+ }+ jv_free(b);+ return a;+}++int jv_object_contains(jv a, jv b) {+ assert(jv_get_kind(a) == JV_KIND_OBJECT);+ assert(jv_get_kind(b) == JV_KIND_OBJECT);+ int r = 1;++ jv_object_foreach(b, key, b_val) {+ jv a_val = jv_object_get(jv_copy(a), jv_copy(key));++ r = jv_contains(a_val, b_val);+ jv_free(key);++ if (!r) break;+ }++ jv_free(a);+ jv_free(b);+ return r;+}++/*+ * Object iteration (internal helpers)+ */++enum { ITER_FINISHED = -2 };++int jv_object_iter_valid(jv object, int i) {+ return i != ITER_FINISHED;+}++int jv_object_iter(jv object) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ return jv_object_iter_next(object, -1);+}++int jv_object_iter_next(jv object, int iter) {+ assert(jv_get_kind(object) == JV_KIND_OBJECT);+ assert(iter != ITER_FINISHED);+ struct object_slot* slot;+ do {+ iter++;+ if (iter >= jvp_object_size(object))+ return ITER_FINISHED;+ slot = jvp_object_get_slot(object, iter);+ } while (jv_get_kind(slot->string) == JV_KIND_NULL);+ assert(jv_get_kind(jvp_object_get_slot(object,iter)->string)+ == JV_KIND_STRING);+ return iter;+}++jv jv_object_iter_key(jv object, int iter) {+ jv s = jvp_object_get_slot(object, iter)->string;+ assert(jv_get_kind(s) == JV_KIND_STRING);+ return jv_copy(s);+}++jv jv_object_iter_value(jv object, int iter) {+ return jv_copy(jvp_object_get_slot(object, iter)->value);+}++/*+ * Memory management+ */+jv jv_copy(jv j) {+ if (jv_get_kind(j) == JV_KIND_ARRAY ||+ jv_get_kind(j) == JV_KIND_STRING ||+ jv_get_kind(j) == JV_KIND_OBJECT ||+ (jv_get_kind(j) == JV_KIND_INVALID && j.u.ptr != 0)) {+ jvp_refcnt_inc(j.u.ptr);+ }+ return j;+}++void jv_free(jv j) {+ if (jv_get_kind(j) == JV_KIND_ARRAY) {+ jvp_array_free(j);+ } else if (jv_get_kind(j) == JV_KIND_STRING) {+ jvp_string_free(j);+ } else if (jv_get_kind(j) == JV_KIND_OBJECT) {+ jvp_object_free(j);+ } else if (jv_get_kind(j) == JV_KIND_INVALID) {+ jvp_invalid_free(j);+ }+}++int jv_get_refcnt(jv j) {+ switch (jv_get_kind(j)) {+ case JV_KIND_ARRAY:+ case JV_KIND_STRING:+ case JV_KIND_OBJECT:+ return j.u.ptr->count;+ default:+ return 1;+ }+}++/*+ * Higher-level operations+ */++int jv_equal(jv a, jv b) {+ int r;+ if (jv_get_kind(a) != jv_get_kind(b)) {+ r = 0;+ } else if (jv_get_kind(a) == JV_KIND_NUMBER) {+ r = jv_number_value(a) == jv_number_value(b);+ } else if (a.kind_flags == b.kind_flags &&+ a.size == b.size &&+ a.u.ptr == b.u.ptr) {+ r = 1;+ } else {+ switch (jv_get_kind(a)) {+ case JV_KIND_ARRAY:+ r = jvp_array_equal(a, b);+ break;+ case JV_KIND_STRING:+ r = jvp_string_equal(a, b);+ break;+ case JV_KIND_OBJECT:+ r = jvp_object_equal(a, b);+ break;+ default:+ r = 1;+ break;+ }+ }+ jv_free(a);+ jv_free(b);+ return r;+}++int jv_identical(jv a, jv b) {+ int r;+ if (a.kind_flags != b.kind_flags+ || a.offset != b.offset+ || a.size != b.size) {+ r = 0;+ } else {+ switch (jv_get_kind(a)) {+ case JV_KIND_ARRAY:+ case JV_KIND_STRING:+ case JV_KIND_OBJECT:+ r = a.u.ptr == b.u.ptr;+ break;+ case JV_KIND_NUMBER:+ r = a.u.number == b.u.number;+ break;+ default:+ r = 1;+ break;+ }+ }+ jv_free(a);+ jv_free(b);+ return r;+}++int jv_contains(jv a, jv b) {+ int r = 1;+ if (jv_get_kind(a) != jv_get_kind(b)) {+ r = 0;+ } else if (jv_get_kind(a) == JV_KIND_OBJECT) {+ r = jv_object_contains(jv_copy(a), jv_copy(b));+ } else if (jv_get_kind(a) == JV_KIND_ARRAY) {+ r = jv_array_contains(jv_copy(a), jv_copy(b));+ } else if (jv_get_kind(a) == JV_KIND_STRING) {+ r = strstr(jv_string_value(a), jv_string_value(b)) != 0;+ } else {+ r = jv_equal(jv_copy(a), jv_copy(b));+ }+ jv_free(a);+ jv_free(b);+ return r;+}
@@ -0,0 +1,240 @@+#ifndef JV_H+#define JV_H++#include <stdarg.h>+#include <stdint.h>+#include <stdio.h>++typedef enum {+ JV_KIND_INVALID,+ JV_KIND_NULL,+ JV_KIND_FALSE,+ JV_KIND_TRUE,+ JV_KIND_NUMBER,+ JV_KIND_STRING,+ JV_KIND_ARRAY,+ JV_KIND_OBJECT+} jv_kind;++struct jv_refcnt;++/* All of the fields of this struct are private.+ Really. Do not play with them. */+typedef struct {+ unsigned char kind_flags;+ unsigned char pad_;+ unsigned short offset; /* array offsets */+ int size;+ union {+ struct jv_refcnt* ptr;+ double number;+ } u;+} jv;++/*+ * All jv_* functions consume (decref) input and produce (incref) output+ * Except jv_copy+ */++jv_kind jv_get_kind(jv);+const char* jv_kind_name(jv_kind);+static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; }++jv jv_copy(jv);+void jv_free(jv);++int jv_get_refcnt(jv);++int jv_equal(jv, jv);+int jv_identical(jv, jv);+int jv_contains(jv, jv);++jv jv_invalid(void);+jv jv_invalid_with_msg(jv);+jv jv_invalid_get_msg(jv);+int jv_invalid_has_msg(jv);+++jv jv_null(void);+jv jv_true(void);+jv jv_false(void);+jv jv_bool(int);++jv jv_number(double);+double jv_number_value(jv);+int jv_is_integer(jv);++jv jv_array(void);+jv jv_array_sized(int);+int jv_array_length(jv);+jv jv_array_get(jv, int);+jv jv_array_set(jv, int, jv);+jv jv_array_append(jv, jv);+jv jv_array_concat(jv, jv);+jv jv_array_slice(jv, int, int);+jv jv_array_indexes(jv, jv);+#define jv_array_foreach(a, i, x) \+ for (int jv_len__ = jv_array_length(jv_copy(a)), i=0, jv_j__ = 1; \+ jv_j__; jv_j__ = 0) \+ for (jv x; \+ i < jv_len__ ? \+ (x = jv_array_get(jv_copy(a), i), 1) : 0; \+ i++)++#define JV_ARRAY_1(e) (jv_array_append(jv_array(),e))+#define JV_ARRAY_2(e1,e2) (jv_array_append(JV_ARRAY_1(e1),e2))+#define JV_ARRAY_3(e1,e2,e3) (jv_array_append(JV_ARRAY_2(e1,e2),e3))+#define JV_ARRAY_4(e1,e2,e3,e4) (jv_array_append(JV_ARRAY_3(e1,e2,e3),e4))+#define JV_ARRAY_5(e1,e2,e3,e4,e5) (jv_array_append(JV_ARRAY_4(e1,e2,e3,e4),e5))+#define JV_ARRAY_6(e1,e2,e3,e4,e5,e6) (jv_array_append(JV_ARRAY_5(e1,e2,e3,e4,e5),e6))+#define JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7) (jv_array_append(JV_ARRAY_6(e1,e2,e3,e4,e5,e6),e7))+#define JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8) (jv_array_append(JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7),e8))+#define JV_ARRAY_9(e1,e2,e3,e4,e5,e6,e7,e8,e9) (jv_array_append(JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8),e9))+#define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME+#define JV_ARRAY(...) \+ JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_9, JV_ARRAY_8, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1, dummy)(__VA_ARGS__)++#ifdef __GNUC__+#define JV_PRINTF_LIKE(fmt_arg_num, args_num) \+ __attribute__ ((__format__( __printf__, fmt_arg_num, args_num)))+#define JV_VPRINTF_LIKE(fmt_arg_num) \+ __attribute__ ((__format__( __printf__, fmt_arg_num, 0)))+#endif+++jv jv_string(const char*);+jv jv_string_sized(const char*, int);+jv jv_string_empty(int len);+int jv_string_length_bytes(jv);+int jv_string_length_codepoints(jv);+unsigned long jv_string_hash(jv);+const char* jv_string_value(jv);+jv jv_string_indexes(jv j, jv k);+jv jv_string_slice(jv j, int start, int end);+jv jv_string_concat(jv, jv);+jv jv_string_vfmt(const char*, va_list) JV_VPRINTF_LIKE(1);+jv jv_string_fmt(const char*, ...) JV_PRINTF_LIKE(1, 2);+jv jv_string_append_codepoint(jv a, uint32_t c);+jv jv_string_append_buf(jv a, const char* buf, int len);+jv jv_string_append_str(jv a, const char* str);+jv jv_string_split(jv j, jv sep);+jv jv_string_explode(jv j);+jv jv_string_implode(jv j);++jv jv_object(void);+jv jv_object_get(jv object, jv key);+jv jv_object_set(jv object, jv key, jv value);+jv jv_object_delete(jv object, jv key);+int jv_object_length(jv object);+jv jv_object_merge(jv, jv);+jv jv_object_merge_recursive(jv, jv);++int jv_object_iter(jv);+int jv_object_iter_next(jv, int);+int jv_object_iter_valid(jv, int);+jv jv_object_iter_key(jv, int);+jv jv_object_iter_value(jv, int);+#define jv_object_foreach(t, k, v) \+ for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \+ for (jv k, v; \+ jv_object_iter_valid((t), jv_i__) ? \+ (k = jv_object_iter_key(t, jv_i__), \+ v = jv_object_iter_value(t, jv_i__), \+ 1) \+ : 0; \+ jv_i__ = jv_object_iter_next(t, jv_i__)) \++#define JV_OBJECT_1(k) (jv_object_set(jv_object(),(k),jv_null()))+#define JV_OBJECT_2(k1,v1) (jv_object_set(jv_object(),(k1),(v1)))+#define JV_OBJECT_3(k1,v1,k2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,jv_null()))+#define JV_OBJECT_4(k1,v1,k2,v2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,v2))+#define JV_OBJECT_5(k1,v1,k2,v2,k3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,jv_null))+#define JV_OBJECT_6(k1,v1,k2,v2,k3,v3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,v3))+#define JV_OBJECT_7(k1,v1,k2,v2,k3,v3,k4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,jv_null()))+#define JV_OBJECT_8(k1,v1,k2,v2,k3,v3,k4,v4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,v4))+#define JV_OBJECT_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME+#define JV_OBJECT(...) \+ JV_OBJECT_IDX(__VA_ARGS__, JV_OBJECT_8, JV_OBJECT_7, JV_OBJECT_6, JV_OBJECT_5, JV_OBJECT_4, JV_OBJECT_3, JV_OBJECT_2, JV_OBJECT_1)(__VA_ARGS__)++++int jv_get_refcnt(jv);++enum jv_print_flags {+ JV_PRINT_PRETTY = 1,+ JV_PRINT_ASCII = 2,+ JV_PRINT_COLOR = 4,+ JV_PRINT_SORTED = 8,+ JV_PRINT_INVALID = 16,+ JV_PRINT_REFCOUNT = 32,+ JV_PRINT_TAB = 64,+ JV_PRINT_ISATTY = 128,+ JV_PRINT_SPACE0 = 256,+ JV_PRINT_SPACE1 = 512,+ JV_PRINT_SPACE2 = 1024,+};+#define JV_PRINT_INDENT_FLAGS(n) \+ ((n) < 0 || (n) > 7 ? JV_PRINT_TAB | JV_PRINT_PRETTY : (n) == 0 ? 0 : (n) << 8 | JV_PRINT_PRETTY)+void jv_dumpf(jv, FILE *f, int flags);+void jv_dump(jv, int flags);+void jv_show(jv, int flags);+jv jv_dump_string(jv, int flags);+char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize);++enum {+ JV_PARSE_SEQ = 1,+ JV_PARSE_STREAMING = 2,+ JV_PARSE_STREAM_ERRORS = 4,+};++jv jv_parse(const char* string);+jv jv_parse_sized(const char* string, int length);++typedef void (*jv_nomem_handler_f)(void *);+void jv_nomem_handler(jv_nomem_handler_f, void *);++jv jv_load_file(const char *, int);++typedef struct jv_parser jv_parser;+jv_parser* jv_parser_new(int);+void jv_parser_set_buf(jv_parser*, const char*, int, int);+int jv_parser_remaining(jv_parser*);+jv jv_parser_next(jv_parser*);+void jv_parser_free(jv_parser*);++jv jv_get(jv, jv);+jv jv_set(jv, jv, jv);+jv jv_has(jv, jv);+jv jv_setpath(jv, jv, jv);+jv jv_getpath(jv, jv);+jv jv_delpaths(jv, jv);+jv jv_keys(jv /*object or array*/);+jv jv_keys_unsorted(jv /*object or array*/);+int jv_cmp(jv, jv);+jv jv_group(jv, jv);+jv jv_sort(jv, jv);+++#endif+++/*++true/false/null:+check kind++number:+introduce/eliminate jv+to integer++array:+copy+free+slice+index+update++updateslice?+++ */
@@ -0,0 +1,179 @@+#include <stdlib.h>+#include <stdio.h>+#include <string.h>+#include "jv_alloc.h"++struct nomem_handler {+ jv_nomem_handler_f handler;+ void *data;+};++#if !defined(HAVE_PTHREAD_KEY_CREATE) || \+ !defined(HAVE_PTHREAD_ONCE) || \+ !defined(HAVE_ATEXIT)++/* Try thread-local storage? */++#ifdef _MSC_VER+/* Visual C++: yes */+static __declspec(thread) struct nomem_handler nomem_handler;+#define USE_TLS+#else+#ifdef HAVE___THREAD+/* GCC and friends: yes */+static __thread struct nomem_handler nomem_handler;+#define USE_TLS+#endif /* HAVE___THREAD */+#endif /* _MSC_VER */++#endif /* !HAVE_PTHREAD_KEY_CREATE */++#ifdef USE_TLS+void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {+ nomem_handler.handler = handler;+}++static void memory_exhausted() {+ if (nomem_handler.handler)+ nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety+ // Or not+ fprintf(stderr, "error: cannot allocate memory\n");+ abort();+}+#else /* USE_TLS */++#ifdef HAVE_PTHREAD_KEY_CREATE+#include <pthread.h>++pthread_key_t nomem_handler_key;+pthread_once_t mem_once = PTHREAD_ONCE_INIT;++static void tsd_fini(void) {+ struct nomem_handler *nomem_handler;+ nomem_handler = pthread_getspecific(nomem_handler_key);+ if (nomem_handler) {+ (void) pthread_setspecific(nomem_handler_key, NULL);+ free(nomem_handler);+ }+}++static void tsd_init(void) {+ if (pthread_key_create(&nomem_handler_key, NULL) != 0) {+ fprintf(stderr, "error: cannot create thread specific key");+ abort();+ }+ if (atexit(tsd_fini) != 0) {+ fprintf(stderr, "error: cannot set an exit handler");+ abort();+ }+ struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));+ if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {+ fprintf(stderr, "error: cannot set thread specific data");+ abort();+ }+}++void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {+ pthread_once(&mem_once, tsd_init); // cannot fail+ struct nomem_handler *nomem_handler;++ nomem_handler = pthread_getspecific(nomem_handler_key);+ if (nomem_handler == NULL) {+ handler(data);+ fprintf(stderr, "error: cannot allocate memory\n");+ abort();+ }+ nomem_handler->handler = handler;+ nomem_handler->data = data;+}++static void memory_exhausted() {+ struct nomem_handler *nomem_handler;++ pthread_once(&mem_once, tsd_init);+ nomem_handler = pthread_getspecific(nomem_handler_key);+ if (nomem_handler)+ nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety+ // Or not+ fprintf(stderr, "error: cannot allocate memory\n");+ abort();+}++#else++/* No thread-local storage of any kind that we know how to handle */++static struct nomem_handler nomem_handler;+void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {+ nomem_handler.handler = handler;+ nomem_handler.data = data;+}++static void memory_exhausted() {+ fprintf(stderr, "error: cannot allocate memory\n");+ abort();+}++#endif /* HAVE_PTHREAD_KEY_CREATE */+#endif /* USE_TLS */+++void* jv_mem_alloc(size_t sz) {+ void* p = malloc(sz);+ if (!p) {+ memory_exhausted();+ }+ return p;+}++void* jv_mem_alloc_unguarded(size_t sz) {+ return malloc(sz);+}++void* jv_mem_calloc(size_t nemb, size_t sz) {+ void* p = calloc(nemb, sz);+ if (!p) {+ memory_exhausted();+ }+ return p;+}++void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) {+ return calloc(nemb, sz);+}++char* jv_mem_strdup(const char *s) {+ char *p = strdup(s);+ if (!p) {+ memory_exhausted();+ }+ return p;+}++char* jv_mem_strdup_unguarded(const char *s) {+ return strdup(s);+}++void jv_mem_free(void* p) {+ free(p);+}++void* jv_mem_realloc(void* p, size_t sz) {+ p = realloc(p, sz);+ if (!p) {+ memory_exhausted();+ }+ return p;+}++#ifndef NDEBUG+volatile char jv_mem_uninitialised;+__attribute__((constructor)) void jv_mem_uninit_setup(){+ // ignore warning that this reads uninitialized memory - that's the point!+#ifndef __clang_analyzer__+ char* p = malloc(1);+ jv_mem_uninitialised = *p;+ free(p);+#endif+}+#endif
@@ -0,0 +1,27 @@+#ifndef JV_ALLOC_H+#define JV_ALLOC_H++#include <stddef.h>+#include "jv.h"++#ifndef NDEBUG+extern volatile char jv_mem_uninitialised;+#endif++static void jv_mem_invalidate(void* mem, size_t n) {+#ifndef NDEBUG+ char* m = mem;+ while (n--) *m++ ^= jv_mem_uninitialised ^ jv_mem_uninitialised;+#endif+}++void* jv_mem_alloc(size_t);+void* jv_mem_alloc_unguarded(size_t);+void* jv_mem_calloc(size_t, size_t);+void* jv_mem_calloc_unguarded(size_t, size_t);+char* jv_mem_strdup(const char *);+char* jv_mem_strdup_unguarded(const char *);+void jv_mem_free(void*);+__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);++#endif
@@ -0,0 +1,624 @@+#include <string.h>+#include <stdlib.h>+#include <assert.h>+#include "jv_alloc.h"++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(j);+ } else if (jv_get_kind(j) == JV_KIND_STRING) {+ len = jv_string_length_codepoints(j);+ } else {+ jv_free(j);+ return 0;+ }+ 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 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(jv_copy(t), k, &start, &end)) {+ v = jv_array_slice(t, start, end);+ } else {+ jv_free(t);+ v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));+ }+ } else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {+ int start, end;+ if (parse_slice(jv_copy(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_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(jv_copy(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(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;+}++jv jv_has(jv t, jv k) {+ assert(jv_is_valid(t));+ assert(jv_is_valid(k));+ jv ret;+ if (jv_get_kind(t) == JV_KIND_NULL) {+ jv_free(t);+ jv_free(k);+ ret = jv_false();+ } else if (jv_get_kind(t) == JV_KIND_OBJECT &&+ jv_get_kind(k) == JV_KIND_STRING) {+ jv elem = jv_object_get(t, k);+ ret = jv_bool(jv_is_valid(elem));+ jv_free(elem);+ } else if (jv_get_kind(t) == JV_KIND_ARRAY &&+ jv_get_kind(k) == JV_KIND_NUMBER) {+ jv elem = jv_array_get(t, (int)jv_number_value(k));+ ret = jv_bool(jv_is_valid(elem));+ jv_free(elem);+ } else {+ ret = jv_invalid_with_msg(jv_string_fmt("Cannot check whether %s has a %s key",+ jv_kind_name(jv_get_kind(t)),+ jv_kind_name(jv_get_kind(k))));+ jv_free(t);+ jv_free(k);+ }+ return ret;+}++// 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(jv_copy(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);+ 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);+ }+ arr_out:+ jv_free(starts);+ jv_free(ends);+ jv_free(orig_keys);+ jv_free(t);+ t = new_array;+ } else if (jv_get_kind(t) == JV_KIND_OBJECT) {+ jv_array_foreach(keys, i, k) {+ if (jv_get_kind(k) != JV_KIND_STRING) {+ jv_free(t);+ t = jv_invalid_with_msg(jv_string_fmt("Cannot delete %s field of object",+ jv_kind_name(jv_get_kind(k))));+ jv_free(k);+ break;+ }+ t = jv_object_delete(t, k);+ }+ } else {+ jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",+ jv_kind_name(jv_get_kind(t))));+ jv_free(t);+ t = err;+ }+ jv_free(keys);+ return t;+}++jv jv_setpath(jv root, jv path, jv value) {+ if (jv_get_kind(path) != JV_KIND_ARRAY) {+ jv_free(value);+ jv_free(root);+ jv_free(path);+ return jv_invalid_with_msg(jv_string("Path must be specified as an array"));+ }+ if (!jv_is_valid(root)){+ jv_free(value);+ jv_free(path);+ return root;+ }+ if (jv_array_length(jv_copy(path)) == 0) {+ jv_free(path);+ jv_free(root);+ return value;+ }+ jv pathcurr = jv_array_get(jv_copy(path), 0);+ jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));+ return jv_set(root, pathcurr,+ jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));+}++jv jv_getpath(jv root, jv path) {+ if (jv_get_kind(path) != JV_KIND_ARRAY) {+ jv_free(root);+ jv_free(path);+ return jv_invalid_with_msg(jv_string("Path must be specified as an array"));+ }+ if (!jv_is_valid(root)) {+ jv_free(path);+ return root;+ }+ if (jv_array_length(jv_copy(path)) == 0) {+ jv_free(path);+ return root;+ }+ jv pathcurr = jv_array_get(jv_copy(path), 0);+ jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));+ return jv_getpath(jv_get(root, pathcurr), pathrest);+}++// assumes paths is a sorted array of arrays+static jv delpaths_sorted(jv object, jv paths, int start) {+ jv delkeys = jv_array();+ for (int i=0; i<jv_array_length(jv_copy(paths));) {+ int j = i;+ assert(jv_array_length(jv_array_get(jv_copy(paths), i)) > start);+ int delkey = jv_array_length(jv_array_get(jv_copy(paths), i)) == start + 1;+ jv key = jv_array_get(jv_array_get(jv_copy(paths), i), start);+ while (j < jv_array_length(jv_copy(paths)) &&+ jv_equal(jv_copy(key), jv_array_get(jv_array_get(jv_copy(paths), j), start)))+ j++;+ // if i <= entry < j, then entry starts with key+ if (delkey) {+ // deleting this entire key, we don't care about any more specific deletions+ delkeys = jv_array_append(delkeys, key);+ } else {+ // deleting certain sub-parts of this key+ jv subobject = jv_get(jv_copy(object), jv_copy(key));+ if (!jv_is_valid(subobject)) {+ jv_free(key);+ jv_free(object);+ object = subobject;+ break;+ } else if (jv_get_kind(subobject) == JV_KIND_NULL) {+ jv_free(key);+ jv_free(subobject);+ } else {+ jv newsubobject = delpaths_sorted(subobject, jv_array_slice(jv_copy(paths), i, j), start+1);+ if (!jv_is_valid(newsubobject)) {+ jv_free(key);+ jv_free(object);+ object = newsubobject;+ break;+ }+ object = jv_set(object, key, newsubobject);+ }+ if (!jv_is_valid(object)) break;+ }+ i = j;+ }+ jv_free(paths);+ if (jv_is_valid(object))+ object = jv_dels(object, delkeys);+ else+ jv_free(delkeys);+ return object;+}++jv jv_delpaths(jv object, jv paths) {+ if (jv_get_kind(paths) != JV_KIND_ARRAY) {+ jv_free(object);+ jv_free(paths);+ return jv_invalid_with_msg(jv_string("Paths must be specified as an array"));+ }+ paths = jv_sort(paths, jv_copy(paths));+ jv_array_foreach(paths, i, elem) {+ if (jv_get_kind(elem) != JV_KIND_ARRAY) {+ jv_free(object);+ jv_free(paths);+ jv err = jv_invalid_with_msg(jv_string_fmt("Path must be specified as array, not %s",+ jv_kind_name(jv_get_kind(elem))));+ jv_free(elem);+ return err;+ }+ jv_free(elem);+ }+ if (jv_array_length(jv_copy(paths)) == 0) {+ // nothing is being deleted+ jv_free(paths);+ return object;+ }+ if (jv_array_length(jv_array_get(jv_copy(paths), 0)) == 0) {+ // everything is being deleted+ jv_free(paths);+ jv_free(object);+ return jv_null();+ }+ return delpaths_sorted(object, paths, 0);+}+++static int string_cmp(const void* pa, const void* pb){+ const jv* a = pa;+ const jv* b = pb;+ int lena = jv_string_length_bytes(jv_copy(*a));+ int lenb = jv_string_length_bytes(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;+}++jv jv_keys_unsorted(jv x) {+ if (jv_get_kind(x) != JV_KIND_OBJECT)+ return jv_keys(x);+ jv answer = jv_array_sized(jv_object_length(jv_copy(x)));+ jv_object_foreach(x, key, value) {+ answer = jv_array_append(answer, key);+ jv_free(value);+ }+ jv_free(x);+ return answer;+}++jv jv_keys(jv x) {+ if (jv_get_kind(x) == JV_KIND_OBJECT) {+ int nkeys = jv_object_length(jv_copy(x));+ jv* keys = jv_mem_alloc(sizeof(jv) * nkeys);+ int kidx = 0;+ jv_object_foreach(x, key, value) {+ keys[kidx++] = key;+ jv_free(value);+ }+ qsort(keys, nkeys, sizeof(jv), string_cmp);+ jv answer = jv_array_sized(nkeys);+ for (int i = 0; i<nkeys; i++) {+ answer = jv_array_append(answer, keys[i]);+ }+ jv_mem_free(keys);+ jv_free(x);+ return answer;+ } else if (jv_get_kind(x) == JV_KIND_ARRAY) {+ int n = jv_array_length(x);+ jv answer = jv_array();+ for (int i=0; i<n; i++){+ answer = jv_array_set(answer, i, jv_number(i));+ }+ return answer;+ } else {+ assert(0 && "jv_keys passed something neither object nor array");+ return jv_invalid();+ }+}++int jv_cmp(jv a, jv b) {+ if (jv_get_kind(a) != jv_get_kind(b)) {+ int r = (int)jv_get_kind(a) - (int)jv_get_kind(b);+ jv_free(a);+ jv_free(b);+ return r;+ }+ int r = 0;+ switch (jv_get_kind(a)) {+ default:+ assert(0 && "invalid kind passed to jv_cmp");+ case JV_KIND_NULL:+ case JV_KIND_FALSE:+ case JV_KIND_TRUE:+ // there's only one of each of these values+ r = 0;+ break;++ case JV_KIND_NUMBER: {+ double da = jv_number_value(a), db = jv_number_value(b);++ // handle NaN as though it were null+ if (da != da) r = jv_cmp(jv_null(), jv_number(db));+ else if (db != db) r = jv_cmp(jv_number(da), jv_null());+ else if (da < db) r = -1;+ else if (da == db) r = 0;+ else r = 1;+ break;+ }++ case JV_KIND_STRING: {+ r = string_cmp(&a, &b);+ break;+ }++ case JV_KIND_ARRAY: {+ // Lexical ordering of arrays+ int i = 0;+ while (r == 0) {+ int a_done = i >= jv_array_length(jv_copy(a));+ int b_done = i >= jv_array_length(jv_copy(b));+ if (a_done || b_done) {+ r = b_done - a_done; //suddenly, logic+ break;+ }+ jv xa = jv_array_get(jv_copy(a), i);+ jv xb = jv_array_get(jv_copy(b), i);+ r = jv_cmp(xa, xb);+ i++;+ }+ break;+ }++ case JV_KIND_OBJECT: {+ jv keys_a = jv_keys(jv_copy(a));+ jv keys_b = jv_keys(jv_copy(b));+ r = jv_cmp(jv_copy(keys_a), keys_b);+ if (r == 0) {+ jv_array_foreach(keys_a, i, key) {+ jv xa = jv_object_get(jv_copy(a), jv_copy(key));+ jv xb = jv_object_get(jv_copy(b), key);+ r = jv_cmp(xa, xb);+ if (r) break;+ }+ }+ jv_free(keys_a);+ break;+ }+ }++ jv_free(a);+ jv_free(b);+ return r;+}+++struct sort_entry {+ jv object;+ jv key;+};++static int sort_cmp(const void* pa, const void* pb) {+ const struct sort_entry* a = pa;+ const struct sort_entry* b = pb;+ int r = jv_cmp(jv_copy(a->key), jv_copy(b->key));+ // comparing by address if r == 0 makes the sort stable+ return r ? r : (int)(a - b);+}++static struct sort_entry* sort_items(jv objects, jv keys) {+ assert(jv_get_kind(objects) == JV_KIND_ARRAY);+ assert(jv_get_kind(keys) == JV_KIND_ARRAY);+ assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));+ int n = jv_array_length(jv_copy(objects));+ struct sort_entry* entries = jv_mem_alloc(sizeof(struct sort_entry) * n);+ for (int i=0; i<n; i++) {+ entries[i].object = jv_array_get(jv_copy(objects), i);+ entries[i].key = jv_array_get(jv_copy(keys), i);+ }+ jv_free(objects);+ jv_free(keys);+ qsort(entries, n, sizeof(struct sort_entry), sort_cmp);+ return entries;+}++jv jv_sort(jv objects, jv keys) {+ assert(jv_get_kind(objects) == JV_KIND_ARRAY);+ assert(jv_get_kind(keys) == JV_KIND_ARRAY);+ assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));+ int n = jv_array_length(jv_copy(objects));+ struct sort_entry* entries = sort_items(objects, keys);+ jv ret = jv_array();+ for (int i=0; i<n; i++) {+ jv_free(entries[i].key);+ ret = jv_array_set(ret, i, entries[i].object);+ }+ jv_mem_free(entries);+ return ret;+}++jv jv_group(jv objects, jv keys) {+ assert(jv_get_kind(objects) == JV_KIND_ARRAY);+ assert(jv_get_kind(keys) == JV_KIND_ARRAY);+ assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));+ int n = jv_array_length(jv_copy(objects));+ struct sort_entry* entries = sort_items(objects, keys);+ jv ret = jv_array();+ if (n > 0) {+ jv curr_key = entries[0].key;+ jv group = jv_array_append(jv_array(), entries[0].object);+ for (int i = 1; i < n; i++) {+ if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {+ jv_free(entries[i].key);+ } else {+ jv_free(curr_key);+ curr_key = entries[i].key;+ ret = jv_array_append(ret, group);+ group = jv_array();+ }+ group = jv_array_append(group, entries[i].object);+ }+ jv_free(curr_key);+ ret = jv_array_append(ret, group);+ }+ jv_mem_free(entries);+ return ret;+}
@@ -0,0 +1,4275 @@++/****************************************************************+ *+ * The author of this software is David M. Gay.+ *+ * Copyright (c) 1991, 2000, 2001 by Lucent Technologies.+ *+ * Permission to use, copy, modify, and distribute this software for any+ * purpose without fee is hereby granted, provided that this entire notice+ * is included in all copies of any software which is or includes a copy+ * or modification of this software and in all copies of the supporting+ * documentation for such software.+ *+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.+ *+ ***************************************************************/++/* Please send bug reports to David M. Gay (dmg at acm dot org,+ * with " at " changed at "@" and " dot " changed to "."). */++/* On a machine with IEEE extended-precision registers, it is+ * necessary to specify double-precision (53-bit) rounding precision+ * before invoking strtod or dtoa. If the machine uses (the equivalent+ * of) Intel 80x87 arithmetic, the call+ * _control87(PC_53, MCW_PC);+ * does this with many compilers. Whether this or another call is+ * appropriate depends on the compiler; for this to work, it may be+ * necessary to #include "float.h" or another system-dependent header+ * file.+ */++/* strtod for IEEE-, VAX-, and IBM-arithmetic machines.+ * (Note that IEEE arithmetic is disabled by gcc's -ffast-math flag.)+ *+ * This strtod returns a nearest machine number to the input decimal+ * string (or sets errno to ERANGE). With IEEE arithmetic, ties are+ * broken by the IEEE round-even rule. Otherwise ties are broken by+ * biased rounding (add half and chop).+ *+ * Inspired loosely by William D. Clinger's paper "How to Read Floating+ * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101].+ *+ * Modifications:+ *+ * 1. We only require IEEE, IBM, or VAX double-precision+ * arithmetic (not IEEE double-extended).+ * 2. We get by with floating-point arithmetic in a case that+ * Clinger missed -- when we're computing d * 10^n+ * for a small integer d and the integer n is not too+ * much larger than 22 (the maximum integer k for which+ * we can represent 10^k exactly), we may be able to+ * compute (d*10^k) * 10^(e-k) with just one roundoff.+ * 3. Rather than a bit-at-a-time adjustment of the binary+ * result in the hard case, we use floating-point+ * arithmetic to determine the adjustment to within+ * one bit; only in really hard cases do we need to+ * compute a second residual.+ * 4. Because of 3., we don't need a large table of powers of 10+ * for ten-to-e (just some small tables, e.g. of 10^k+ * for 0 <= k <= 22).+ */++/*+ * #define IEEE_8087 for IEEE-arithmetic machines where the least+ * significant byte has the lowest address.+ * #define IEEE_MC68k for IEEE-arithmetic machines where the most+ * significant byte has the lowest address.+ * #define Long int on machines with 32-bit ints and 64-bit longs.+ * #define IBM for IBM mainframe-style floating-point arithmetic.+ * #define VAX for VAX-style floating-point arithmetic (D_floating).+ * #define No_leftright to omit left-right logic in fast floating-point+ * computation of dtoa. This will cause dtoa modes 4 and 5 to be+ * treated the same as modes 2 and 3 for some inputs.+ * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3+ * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS+ * is also #defined, fegetround() will be queried for the rounding mode.+ * Note that both FLT_ROUNDS and fegetround() are specified by the C99+ * standard (and are specified to be consistent, with fesetround()+ * affecting the value of FLT_ROUNDS), but that some (Linux) systems+ * do not work correctly in this regard, so using fegetround() is more+ * portable than using FLT_ROUNDS directly.+ * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3+ * and Honor_FLT_ROUNDS is not #defined.+ * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines+ * that use extended-precision instructions to compute rounded+ * products and quotients) with IBM.+ * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic+ * that rounds toward +Infinity.+ * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased+ * rounding when the underlying floating-point arithmetic uses+ * unbiased rounding. This prevent using ordinary floating-point+ * arithmetic when the result could be computed with one rounding error.+ * #define Inaccurate_Divide for IEEE-format with correctly rounded+ * products but inaccurate quotients, e.g., for Intel i860.+ * #define NO_LONG_LONG on machines that do not have a "long long"+ * integer type (of >= 64 bits). On such machines, you can+ * #define Just_16 to store 16 bits per 32-bit Long when doing+ * high-precision integer arithmetic. Whether this speeds things+ * up or slows things down depends on the machine and the number+ * being converted. If long long is available and the name is+ * something other than "long long", #define Llong to be the name,+ * and if "unsigned Llong" does not work as an unsigned version of+ * Llong, #define #ULLong to be the corresponding unsigned type.+ * #define KR_headers for old-style C function headers.+ * #define Bad_float_h if your system lacks a float.h or if it does not+ * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP,+ * FLT_RADIX, FLT_ROUNDS, and DBL_MAX.+ * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n)+ * if memory is available and otherwise does something you deem+ * appropriate. If MALLOC is undefined, malloc will be invoked+ * directly -- and assumed always to succeed. Similarly, if you+ * want something other than the system's free() to be called to+ * recycle memory acquired from MALLOC, #define FREE to be the+ * name of the alternate routine. (FREE or free is only called in+ * pathological cases, e.g., in a dtoa call after a dtoa return in+ * mode 3 with thousands of digits requested.)+ * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making+ * memory allocations from a private pool of memory when possible.+ * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes,+ * unless #defined to be a different length. This default length+ * suffices to get rid of MALLOC calls except for unusual cases,+ * such as decimal-to-binary conversion of a very long string of+ * digits. The longest string dtoa can return is about 751 bytes+ * long. For conversions by strtod of strings of 800 digits and+ * all dtoa conversions in single-threaded executions with 8-byte+ * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte+ * pointers, PRIVATE_MEM >= 7112 appears adequate.+ * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK+ * #defined automatically on IEEE systems. On such systems,+ * when INFNAN_CHECK is #defined, strtod checks+ * for Infinity and NaN (case insensitively). On some systems+ * (e.g., some HP systems), it may be necessary to #define NAN_WORD0+ * appropriately -- to the most significant word of a quiet NaN.+ * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.)+ * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined,+ * strtod also accepts (case insensitively) strings of the form+ * NaN(x), where x is a string of hexadecimal digits and spaces;+ * if there is only one string of hexadecimal digits, it is taken+ * for the 52 fraction bits of the resulting NaN; if there are two+ * or more strings of hex digits, the first is for the high 20 bits,+ * the second and subsequent for the low 32 bits, with intervening+ * white space ignored; but if this results in none of the 52+ * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0+ * and NAN_WORD1 are used instead.+ * #define MULTIPLE_THREADS if the system offers preemptively scheduled+ * multiple threads. In this case, you must provide (or suitably+ * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed+ * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed+ * in pow5mult, ensures lazy evaluation of only one copy of high+ * powers of 5; omitting this lock would introduce a small+ * probability of wasting memory, but would otherwise be harmless.)+ * You must also invoke freedtoa(s) to free the value s returned by+ * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined.+ * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that+ * avoids underflows on inputs whose result does not underflow.+ * If you #define NO_IEEE_Scale on a machine that uses IEEE-format+ * floating-point numbers and flushes underflows to zero rather+ * than implementing gradual underflow, then you must also #define+ * Sudden_Underflow.+ * #define USE_LOCALE to use the current locale's decimal_point value.+ * #define SET_INEXACT if IEEE arithmetic is being used and extra+ * computation should be done to set the inexact flag when the+ * result is inexact and avoid setting inexact when the result+ * is exact. In this case, dtoa.c must be compiled in+ * an environment, perhaps provided by #include "dtoa.c" in a+ * suitable wrapper, that defines two functions,+ * int get_inexact(void);+ * void clear_inexact(void);+ * such that get_inexact() returns a nonzero value if the+ * inexact bit is already set, and clear_inexact() sets the+ * inexact bit to 0. When SET_INEXACT is #defined, strtod+ * also does extra computations to set the underflow and overflow+ * flags when appropriate (i.e., when the result is tiny and+ * inexact or when it is a numeric value rounded to +-infinity).+ * #define NO_ERRNO if strtod should not assign errno = ERANGE when+ * the result overflows to +-Infinity or underflows to 0.+ * #define NO_HEX_FP to omit recognition of hexadecimal floating-point+ * values by strtod.+ * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now)+ * to disable logic for "fast" testing of very long input strings+ * to strtod. This testing proceeds by initially truncating the+ * input string, then if necessary comparing the whole string with+ * a decimal expansion to decide close cases. This logic is only+ * used for input more than STRTOD_DIGLIM digits long (default 40).+ */++#define NO_ERRNO+#define NO_HEX_FP+#define No_Hex_NaN+#define Long int++#include "jv_dtoa.h"++#include "jv_alloc.h"+#define MALLOC jv_mem_alloc+#define FREE jv_mem_free++++#ifndef Long+#define Long long+#endif+#ifndef ULong+typedef unsigned Long ULong;+#endif++#ifdef DEBUG+#include "stdio.h"+#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);}+#endif++#include "stdlib.h"+#include "string.h"++#ifdef USE_LOCALE+#include "locale.h"+#endif++#ifdef Honor_FLT_ROUNDS+#ifndef Trust_FLT_ROUNDS+#include <fenv.h>+#endif+#endif++#ifdef MALLOC+extern void *MALLOC(size_t);+#else+#define MALLOC malloc+#endif++#undef IEEE_Arith+#undef Avoid_Underflow+#ifdef IEEE_MC68k+#define IEEE_Arith+#endif+#ifdef IEEE_8087+#define IEEE_Arith+#endif++#ifdef IEEE_Arith+#ifndef NO_INFNAN_CHECK+#undef INFNAN_CHECK+#define INFNAN_CHECK+#endif+#else+#undef INFNAN_CHECK+#define NO_STRTOD_BIGCOMP+#endif++#include "errno.h"++#ifdef Bad_float_h++#ifdef IEEE_Arith+#define DBL_DIG 15+#define DBL_MAX_10_EXP 308+#define DBL_MAX_EXP 1024+#define FLT_RADIX 2+#endif /*IEEE_Arith*/++#ifdef IBM+#define DBL_DIG 16+#define DBL_MAX_10_EXP 75+#define DBL_MAX_EXP 63+#define FLT_RADIX 16+#define DBL_MAX 7.2370055773322621e+75+#endif++#ifdef VAX+#define DBL_DIG 16+#define DBL_MAX_10_EXP 38+#define DBL_MAX_EXP 127+#define FLT_RADIX 2+#define DBL_MAX 1.7014118346046923e+38+#endif++#ifndef LONG_MAX+#define LONG_MAX 2147483647+#endif++#else /* ifndef Bad_float_h */+#include "float.h"+#endif /* Bad_float_h */++#ifndef __MATH_H__+#include "math.h"+#endif++#ifdef __cplusplus+extern "C" {+#endif++#ifndef CONST+#define CONST const+#endif++#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1+Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined.+#endif++typedef union { double d; ULong L[2]; } U;++#ifdef IEEE_8087+#define word0(x) (x)->L[1]+#define word1(x) (x)->L[0]+#else+#define word0(x) (x)->L[0]+#define word1(x) (x)->L[1]+#endif+#define dval(x) (x)->d++#ifndef STRTOD_DIGLIM+#define STRTOD_DIGLIM 40+#endif++#ifdef DIGLIM_DEBUG+extern int strtod_diglim;+#else+#define strtod_diglim STRTOD_DIGLIM+#endif++/* The following definition of Storeinc is appropriate for MIPS processors.+ * An alternative that might be better on some machines is+ * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff)+ */+#if defined(IEEE_8087) + defined(VAX)+#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \+((unsigned short *)a)[0] = (unsigned short)c, a++)+#else+#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \+((unsigned short *)a)[1] = (unsigned short)c, a++)+#endif++/* #define P DBL_MANT_DIG */+/* Ten_pmax = floor(P*log(2)/log(5)) */+/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */+/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */+/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */++#ifdef IEEE_Arith+#define Exp_shift 20+#define Exp_shift1 20+#define Exp_msk1 0x100000+#define Exp_msk11 0x100000+#define Exp_mask 0x7ff00000+#define P 53+#define Nbits 53+#define Bias 1023+#define Emax 1023+#define Emin (-1022)+#define Exp_1 0x3ff00000+#define Exp_11 0x3ff00000+#define Ebits 11+#define Frac_mask 0xfffff+#define Frac_mask1 0xfffff+#define Ten_pmax 22+#define Bletch 0x10+#define Bndry_mask 0xfffff+#define Bndry_mask1 0xfffff+#define LSB 1+#define Sign_bit 0x80000000+#define Log2P 1+#define Tiny0 0+#define Tiny1 1+#define Quick_max 14+#define Int_max 14+#ifndef NO_IEEE_Scale+#define Avoid_Underflow+#ifdef Flush_Denorm /* debugging option */+#undef Sudden_Underflow+#endif+#endif++#ifndef Flt_Rounds+#ifdef FLT_ROUNDS+#define Flt_Rounds FLT_ROUNDS+#else+#define Flt_Rounds 1+#endif+#endif /*Flt_Rounds*/++#ifdef Honor_FLT_ROUNDS+#undef Check_FLT_ROUNDS+#define Check_FLT_ROUNDS+#else+#define Rounding Flt_Rounds+#endif++#else /* ifndef IEEE_Arith */+#undef Check_FLT_ROUNDS+#undef Honor_FLT_ROUNDS+#undef SET_INEXACT+#undef Sudden_Underflow+#define Sudden_Underflow+#ifdef IBM+#undef Flt_Rounds+#define Flt_Rounds 0+#define Exp_shift 24+#define Exp_shift1 24+#define Exp_msk1 0x1000000+#define Exp_msk11 0x1000000+#define Exp_mask 0x7f000000+#define P 14+#define Nbits 56+#define Bias 65+#define Emax 248+#define Emin (-260)+#define Exp_1 0x41000000+#define Exp_11 0x41000000+#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */+#define Frac_mask 0xffffff+#define Frac_mask1 0xffffff+#define Bletch 4+#define Ten_pmax 22+#define Bndry_mask 0xefffff+#define Bndry_mask1 0xffffff+#define LSB 1+#define Sign_bit 0x80000000+#define Log2P 4+#define Tiny0 0x100000+#define Tiny1 0+#define Quick_max 14+#define Int_max 15+#else /* VAX */+#undef Flt_Rounds+#define Flt_Rounds 1+#define Exp_shift 23+#define Exp_shift1 7+#define Exp_msk1 0x80+#define Exp_msk11 0x800000+#define Exp_mask 0x7f80+#define P 56+#define Nbits 56+#define Bias 129+#define Emax 126+#define Emin (-129)+#define Exp_1 0x40800000+#define Exp_11 0x4080+#define Ebits 8+#define Frac_mask 0x7fffff+#define Frac_mask1 0xffff007f+#define Ten_pmax 24+#define Bletch 2+#define Bndry_mask 0xffff007f+#define Bndry_mask1 0xffff007f+#define LSB 0x10000+#define Sign_bit 0x8000+#define Log2P 1+#define Tiny0 0x80+#define Tiny1 0+#define Quick_max 15+#define Int_max 15+#endif /* IBM, VAX */+#endif /* IEEE_Arith */++#ifndef IEEE_Arith+#define ROUND_BIASED+#else+#ifdef ROUND_BIASED_without_Round_Up+#undef ROUND_BIASED+#define ROUND_BIASED+#endif+#endif++#ifdef RND_PRODQUOT+#define rounded_product(a,b) a = rnd_prod(a, b)+#define rounded_quotient(a,b) a = rnd_quot(a, b)+extern double rnd_prod(double, double), rnd_quot(double, double);+#else+#define rounded_product(a,b) a *= b+#define rounded_quotient(a,b) a /= b+#endif++#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1))+#define Big1 0xffffffff++#ifndef Pack_32+#define Pack_32+#endif++typedef struct BCinfo BCinfo;+ struct+BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; };++#define FFFFFFFF 0xffffffffUL++#ifdef NO_LONG_LONG+#undef ULLong+#ifdef Just_16+#undef Pack_32+/* When Pack_32 is not defined, we store 16 bits per 32-bit Long.+ * This makes some inner loops simpler and sometimes saves work+ * during multiplications, but it often seems to make things slightly+ * slower. Hence the default is now to store 32 bits per Long.+ */+#endif+#else /* long long available */+#ifndef Llong+#define Llong long long+#endif+#ifndef ULLong+#define ULLong unsigned Llong+#endif+#endif /* NO_LONG_LONG */++++ struct+Bigint {+ struct Bigint *next;+ int k, maxwds, sign, wds;+ ULong x[1];+ };++ typedef struct Bigint Bigint;+++void jvp_dtoa_context_init(struct dtoa_context* C) {+ int i;+ for (i=0; i<(int)(sizeof(C->freelist)/sizeof(C->freelist[0])); i++) {+ C->freelist[i] = 0;+ }+ C->p5s = 0;+}++ static Bigint *+ Balloc(struct dtoa_context* C, int k)+{+ int x;+ Bigint *rv;++ /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */+ /* but this case seems very unlikely. */+ if (k <= Kmax && (rv = C->freelist[k]))+ C->freelist[k] = rv->next;+ else {+ x = 1 << k;+ rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong));+ rv->k = k;+ rv->maxwds = x;+ }+ rv->sign = rv->wds = 0;+ return rv;+ }++ static void+Bfree+ (struct dtoa_context* C, Bigint *v)+{+ if (v) {+ if (v->k > Kmax)+#ifdef FREE+ FREE((void*)v);+#else+ free((void*)v);+#endif+ else {+ v->next = C->freelist[v->k];+ C->freelist[v->k] = v;+ }+ }+ }+++void jvp_dtoa_context_free(struct dtoa_context* C) {+ int k;+ while (C->p5s) {+ Bigint* p5 = C->p5s;+ C->p5s = p5->next;+ Bfree(C, p5);+ }+ for (k=0; k<(int)(sizeof(C->freelist)/sizeof(C->freelist[0])); k++) {+ while (C->freelist[k]) {+ Bigint* v = C->freelist[k];+ C->freelist[k] = v->next;+ FREE(v);+ }+ }+}+++#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \+y->wds*sizeof(Long) + 2*sizeof(int))++ static Bigint *+multadd+ (struct dtoa_context* C, Bigint *b, int m, int a) /* multiply by m and add a */+{+ int i, wds;+#ifdef ULLong+ ULong *x;+ ULLong carry, y;+#else+ ULong carry, *x, y;+#ifdef Pack_32+ ULong xi, z;+#endif+#endif+ Bigint *b1;++ wds = b->wds;+ x = b->x;+ i = 0;+ carry = a;+ do {+#ifdef ULLong+ y = *x * (ULLong)m + carry;+ carry = y >> 32;+ *x++ = y & FFFFFFFF;+#else+#ifdef Pack_32+ xi = *x;+ y = (xi & 0xffff) * m + carry;+ z = (xi >> 16) * m + (y >> 16);+ carry = z >> 16;+ *x++ = (z << 16) + (y & 0xffff);+#else+ y = *x * m + carry;+ carry = y >> 16;+ *x++ = y & 0xffff;+#endif+#endif+ }+ while(++i < wds);+ if (carry) {+ if (wds >= b->maxwds) {+ b1 = Balloc(C, b->k+1);+ Bcopy(b1, b);+ Bfree(C, b);+ b = b1;+ }+ b->x[wds++] = carry;+ b->wds = wds;+ }+ return b;+ }++ static Bigint *+s2b+ (struct dtoa_context* C, const char *s, int nd0, int nd, ULong y9, int dplen)+{+ Bigint *b;+ int i, k;+ Long x, y;++ x = (nd + 8) / 9;+ for(k = 0, y = 1; x > y; y <<= 1, k++) ;+#ifdef Pack_32+ b = Balloc(C, k);+ b->x[0] = y9;+ b->wds = 1;+#else+ b = Balloc(C, k+1);+ b->x[0] = y9 & 0xffff;+ b->wds = (b->x[1] = y9 >> 16) ? 2 : 1;+#endif++ i = 9;+ if (9 < nd0) {+ s += 9;+ do b = multadd(C, b, 10, *s++ - '0');+ while(++i < nd0);+ s += dplen;+ }+ else+ s += dplen + 9;+ for(; i < nd; i++)+ b = multadd(C, b, 10, *s++ - '0');+ return b;+ }++ static int+hi0bits+ (struct dtoa_context* C, ULong x)+{+ int k = 0;++ if (!(x & 0xffff0000)) {+ k = 16;+ x <<= 16;+ }+ if (!(x & 0xff000000)) {+ k += 8;+ x <<= 8;+ }+ if (!(x & 0xf0000000)) {+ k += 4;+ x <<= 4;+ }+ if (!(x & 0xc0000000)) {+ k += 2;+ x <<= 2;+ }+ if (!(x & 0x80000000)) {+ k++;+ if (!(x & 0x40000000))+ return 32;+ }+ return k;+ }++ static int+lo0bits+ (struct dtoa_context* C, ULong *y)+{+ int k;+ ULong x = *y;++ if (x & 7) {+ if (x & 1)+ return 0;+ if (x & 2) {+ *y = x >> 1;+ return 1;+ }+ *y = x >> 2;+ return 2;+ }+ k = 0;+ if (!(x & 0xffff)) {+ k = 16;+ x >>= 16;+ }+ if (!(x & 0xff)) {+ k += 8;+ x >>= 8;+ }+ if (!(x & 0xf)) {+ k += 4;+ x >>= 4;+ }+ if (!(x & 0x3)) {+ k += 2;+ x >>= 2;+ }+ if (!(x & 1)) {+ k++;+ x >>= 1;+ if (!x)+ return 32;+ }+ *y = x;+ return k;+ }++ static Bigint *+i2b+ (struct dtoa_context* C, int i)+{+ Bigint *b;++ b = Balloc(C, 1);+ b->x[0] = i;+ b->wds = 1;+ return b;+ }++ static Bigint *+mult+ (struct dtoa_context* C, Bigint *a, Bigint *b)+{+ Bigint *c;+ int k, wa, wb, wc;+ ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0;+ ULong y;+#ifdef ULLong+ ULLong carry, z;+#else+ ULong carry, z;+#ifdef Pack_32+ ULong z2;+#endif+#endif++ if (a->wds < b->wds) {+ c = a;+ a = b;+ b = c;+ }+ k = a->k;+ wa = a->wds;+ wb = b->wds;+ wc = wa + wb;+ if (wc > a->maxwds)+ k++;+ c = Balloc(C, k);+ for(x = c->x, xa = x + wc; x < xa; x++)+ *x = 0;+ xa = a->x;+ xae = xa + wa;+ xb = b->x;+ xbe = xb + wb;+ xc0 = c->x;+#ifdef ULLong+ for(; xb < xbe; xc0++) {+ if ((y = *xb++)) {+ x = xa;+ xc = xc0;+ carry = 0;+ do {+ z = *x++ * (ULLong)y + *xc + carry;+ carry = z >> 32;+ *xc++ = z & FFFFFFFF;+ }+ while(x < xae);+ *xc = carry;+ }+ }+#else+#ifdef Pack_32+ for(; xb < xbe; xb++, xc0++) {+ if (y = *xb & 0xffff) {+ x = xa;+ xc = xc0;+ carry = 0;+ do {+ z = (*x & 0xffff) * y + (*xc & 0xffff) + carry;+ carry = z >> 16;+ z2 = (*x++ >> 16) * y + (*xc >> 16) + carry;+ carry = z2 >> 16;+ Storeinc(xc, z2, z);+ }+ while(x < xae);+ *xc = carry;+ }+ if (y = *xb >> 16) {+ x = xa;+ xc = xc0;+ carry = 0;+ z2 = *xc;+ do {+ z = (*x & 0xffff) * y + (*xc >> 16) + carry;+ carry = z >> 16;+ Storeinc(xc, z, z2);+ z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry;+ carry = z2 >> 16;+ }+ while(x < xae);+ *xc = z2;+ }+ }+#else+ for(; xb < xbe; xc0++) {+ if (y = *xb++) {+ x = xa;+ xc = xc0;+ carry = 0;+ do {+ z = *x++ * y + *xc + carry;+ carry = z >> 16;+ *xc++ = z & 0xffff;+ }+ while(x < xae);+ *xc = carry;+ }+ }+#endif+#endif+ for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ;+ c->wds = wc;+ return c;+ }+++ static Bigint *+pow5mult+ (struct dtoa_context* C, Bigint *b, int k)+{+ Bigint *b1, *p5, *p51;+ int i;+ static const int p05[3] = { 5, 25, 125 };++ if ((i = k & 3))+ b = multadd(C, b, p05[i-1], 0);++ if (!(k >>= 2))+ return b;+ if (!(p5 = C->p5s)) {+ /* first time */+ p5 = C->p5s = i2b(C, 625);+ p5->next = 0;+ }+ for(;;) {+ if (k & 1) {+ b1 = mult(C, b, p5);+ Bfree(C, b);+ b = b1;+ }+ if (!(k >>= 1))+ break;+ if (!(p51 = p5->next)) {+ p51 = p5->next = mult(C, p5,p5);+ p51->next = 0;+ }+ p5 = p51;+ }+ return b;+ }++ static Bigint *+lshift+ (struct dtoa_context* C, Bigint *b, int k)+{+ int i, k1, n, n1;+ Bigint *b1;+ ULong *x, *x1, *xe, z;++#ifdef Pack_32+ n = k >> 5;+#else+ n = k >> 4;+#endif+ k1 = b->k;+ n1 = n + b->wds + 1;+ for(i = b->maxwds; n1 > i; i <<= 1)+ k1++;+ b1 = Balloc(C, k1);+ x1 = b1->x;+ for(i = 0; i < n; i++)+ *x1++ = 0;+ x = b->x;+ xe = x + b->wds;+#ifdef Pack_32+ if (k &= 0x1f) {+ k1 = 32 - k;+ z = 0;+ do {+ *x1++ = *x << k | z;+ z = *x++ >> k1;+ }+ while(x < xe);+ if ((*x1 = z))+ ++n1;+ }+#else+ if (k &= 0xf) {+ k1 = 16 - k;+ z = 0;+ do {+ *x1++ = *x << k & 0xffff | z;+ z = *x++ >> k1;+ }+ while(x < xe);+ if (*x1 = z)+ ++n1;+ }+#endif+ else do+ *x1++ = *x++;+ while(x < xe);+ b1->wds = n1 - 1;+ Bfree(C, b);+ return b1;+ }++ static int+cmp+ (struct dtoa_context* C, Bigint *a, Bigint *b)+{+ ULong *xa, *xa0, *xb, *xb0;+ int i, j;++ i = a->wds;+ j = b->wds;+#ifdef DEBUG+ if (i > 1 && !a->x[i-1])+ Bug("cmp called with a->x[a->wds-1] == 0");+ if (j > 1 && !b->x[j-1])+ Bug("cmp called with b->x[b->wds-1] == 0");+#endif+ if (i -= j)+ return i;+ xa0 = a->x;+ xa = xa0 + j;+ xb0 = b->x;+ xb = xb0 + j;+ for(;;) {+ if (*--xa != *--xb)+ return *xa < *xb ? -1 : 1;+ if (xa <= xa0)+ break;+ }+ return 0;+ }++ static Bigint *+diff+ (struct dtoa_context* C, Bigint *a, Bigint *b)+{+ Bigint *c;+ int i, wa, wb;+ ULong *xa, *xae, *xb, *xbe, *xc;+#ifdef ULLong+ ULLong borrow, y;+#else+ ULong borrow, y;+#ifdef Pack_32+ ULong z;+#endif+#endif++ i = cmp(C, a,b);+ if (!i) {+ c = Balloc(C, 0);+ c->wds = 1;+ c->x[0] = 0;+ return c;+ }+ if (i < 0) {+ c = a;+ a = b;+ b = c;+ i = 1;+ }+ else+ i = 0;+ c = Balloc(C, a->k);+ c->sign = i;+ wa = a->wds;+ xa = a->x;+ xae = xa + wa;+ wb = b->wds;+ xb = b->x;+ xbe = xb + wb;+ xc = c->x;+ borrow = 0;+#ifdef ULLong+ do {+ y = (ULLong)*xa++ - *xb++ - borrow;+ borrow = y >> 32 & (ULong)1;+ *xc++ = y & FFFFFFFF;+ }+ while(xb < xbe);+ while(xa < xae) {+ y = *xa++ - borrow;+ borrow = y >> 32 & (ULong)1;+ *xc++ = y & FFFFFFFF;+ }+#else+#ifdef Pack_32+ do {+ y = (*xa & 0xffff) - (*xb & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ z = (*xa++ >> 16) - (*xb++ >> 16) - borrow;+ borrow = (z & 0x10000) >> 16;+ Storeinc(xc, z, y);+ }+ while(xb < xbe);+ while(xa < xae) {+ y = (*xa & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ z = (*xa++ >> 16) - borrow;+ borrow = (z & 0x10000) >> 16;+ Storeinc(xc, z, y);+ }+#else+ do {+ y = *xa++ - *xb++ - borrow;+ borrow = (y & 0x10000) >> 16;+ *xc++ = y & 0xffff;+ }+ while(xb < xbe);+ while(xa < xae) {+ y = *xa++ - borrow;+ borrow = (y & 0x10000) >> 16;+ *xc++ = y & 0xffff;+ }+#endif+#endif+ while(!*--xc)+ wa--;+ c->wds = wa;+ return c;+ }++ static double+ulp+ (struct dtoa_context* C, U *x)+{+ Long L;+ U u;++ L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1;+#ifndef Avoid_Underflow+#ifndef Sudden_Underflow+ if (L > 0) {+#endif+#endif+#ifdef IBM+ L |= Exp_msk1 >> 4;+#endif+ word0(&u) = L;+ word1(&u) = 0;+#ifndef Avoid_Underflow+#ifndef Sudden_Underflow+ }+ else {+ L = -L >> Exp_shift;+ if (L < Exp_shift) {+ word0(&u) = 0x80000 >> L;+ word1(&u) = 0;+ }+ else {+ word0(&u) = 0;+ L -= Exp_shift;+ word1(&u) = L >= 31 ? 1 : 1 << 31 - L;+ }+ }+#endif+#endif+ return dval(&u);+ }++ static double+b2d+ (struct dtoa_context* C, Bigint *a, int *e)+{+ ULong *xa, *xa0, w, y, z;+ int k;+ U d;+#ifdef VAX+ ULong d0, d1;+#else+#define d0 word0(&d)+#define d1 word1(&d)+#endif++ xa0 = a->x;+ xa = xa0 + a->wds;+ y = *--xa;+#ifdef DEBUG+ if (!y) Bug("zero y in b2d");+#endif+ k = hi0bits(C, y);+ *e = 32 - k;+#ifdef Pack_32+ if (k < Ebits) {+ d0 = Exp_1 | y >> (Ebits - k);+ w = xa > xa0 ? *--xa : 0;+ d1 = y << ((32-Ebits) + k) | w >> (Ebits - k);+ goto ret_d;+ }+ z = xa > xa0 ? *--xa : 0;+ if (k -= Ebits) {+ d0 = Exp_1 | y << k | z >> (32 - k);+ y = xa > xa0 ? *--xa : 0;+ d1 = z << k | y >> (32 - k);+ }+ else {+ d0 = Exp_1 | y;+ d1 = z;+ }+#else+ if (k < Ebits + 16) {+ z = xa > xa0 ? *--xa : 0;+ d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k;+ w = xa > xa0 ? *--xa : 0;+ y = xa > xa0 ? *--xa : 0;+ d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k;+ goto ret_d;+ }+ z = xa > xa0 ? *--xa : 0;+ w = xa > xa0 ? *--xa : 0;+ k -= Ebits + 16;+ d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k;+ y = xa > xa0 ? *--xa : 0;+ d1 = w << k + 16 | y << k;+#endif+ ret_d:+#ifdef VAX+ word0(&d) = d0 >> 16 | d0 << 16;+ word1(&d) = d1 >> 16 | d1 << 16;+#else+#undef d0+#undef d1+#endif+ return dval(&d);+ }++ static Bigint *+d2b+ (struct dtoa_context* C, U *d, int *e, int *bits)+{+ Bigint *b;+ int de, k;+ ULong *x, y, z;+#ifndef Sudden_Underflow+ int i;+#endif+#ifdef VAX+ ULong d0, d1;+ d0 = word0(d) >> 16 | word0(d) << 16;+ d1 = word1(d) >> 16 | word1(d) << 16;+#else+#define d0 word0(d)+#define d1 word1(d)+#endif++#ifdef Pack_32+ b = Balloc(C, 1);+#else+ b = Balloc(C, 2);+#endif+ x = b->x;++ z = d0 & Frac_mask;+ d0 &= 0x7fffffff; /* clear sign bit, which we ignore */+#ifdef Sudden_Underflow+ de = (int)(d0 >> Exp_shift);+#ifndef IBM+ z |= Exp_msk11;+#endif+#else+ if ((de = (int)(d0 >> Exp_shift)))+ z |= Exp_msk1;+#endif+#ifdef Pack_32+ if ((y = d1)) {+ if ((k = lo0bits(C, &y))) {+ x[0] = y | z << (32 - k);+ z >>= k;+ }+ else+ x[0] = y;+#ifndef Sudden_Underflow+ i =+#endif+ b->wds = (x[1] = z) ? 2 : 1;+ }+ else {+ k = lo0bits(C, &z);+ x[0] = z;+#ifndef Sudden_Underflow+ i =+#endif+ b->wds = 1;+ k += 32;+ }+#else+ if (y = d1) {+ if (k = lo0bits(C, &y))+ if (k >= 16) {+ x[0] = y | z << 32 - k & 0xffff;+ x[1] = z >> k - 16 & 0xffff;+ x[2] = z >> k;+ i = 2;+ }+ else {+ x[0] = y & 0xffff;+ x[1] = y >> 16 | z << 16 - k & 0xffff;+ x[2] = z >> k & 0xffff;+ x[3] = z >> k+16;+ i = 3;+ }+ else {+ x[0] = y & 0xffff;+ x[1] = y >> 16;+ x[2] = z & 0xffff;+ x[3] = z >> 16;+ i = 3;+ }+ }+ else {+#ifdef DEBUG+ if (!z)+ Bug("Zero passed to d2b");+#endif+ k = lo0bits(C, &z);+ if (k >= 16) {+ x[0] = z;+ i = 0;+ }+ else {+ x[0] = z & 0xffff;+ x[1] = z >> 16;+ i = 1;+ }+ k += 32;+ }+ while(!x[i])+ --i;+ b->wds = i + 1;+#endif+#ifndef Sudden_Underflow+ if (de) {+#endif+#ifdef IBM+ *e = (de - Bias - (P-1) << 2) + k;+ *bits = 4*P + 8 - k - hi0bits(C, word0(d) & Frac_mask);+#else+ *e = de - Bias - (P-1) + k;+ *bits = P - k;+#endif+#ifndef Sudden_Underflow+ }+ else {+ *e = de - Bias - (P-1) + 1 + k;+#ifdef Pack_32+ *bits = 32*i - hi0bits(C, x[i-1]);+#else+ *bits = (i+2)*16 - hi0bits(C, x[i]);+#endif+ }+#endif+ return b;+ }+#undef d0+#undef d1++ static double+ratio+ (struct dtoa_context* C, Bigint *a, Bigint *b)+{+ U da, db;+ int k, ka, kb;++ dval(&da) = b2d(C, a, &ka);+ dval(&db) = b2d(C, b, &kb);+#ifdef Pack_32+ k = ka - kb + 32*(a->wds - b->wds);+#else+ k = ka - kb + 16*(a->wds - b->wds);+#endif+#ifdef IBM+ if (k > 0) {+ word0(&da) += (k >> 2)*Exp_msk1;+ if (k &= 3)+ dval(&da) *= 1 << k;+ }+ else {+ k = -k;+ word0(&db) += (k >> 2)*Exp_msk1;+ if (k &= 3)+ dval(&db) *= 1 << k;+ }+#else+ if (k > 0)+ word0(&da) += k*Exp_msk1;+ else {+ k = -k;+ word0(&db) += k*Exp_msk1;+ }+#endif+ return dval(&da) / dval(&db);+ }++ static CONST double+tens[] = {+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,+ 1e20, 1e21, 1e22+#ifdef VAX+ , 1e23, 1e24+#endif+ };++ static CONST double+#ifdef IEEE_Arith+bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 };+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128,+#ifdef Avoid_Underflow+ 9007199254740992.*9007199254740992.e-256+ /* = 2^106 * 1e-256 */+#else+ 1e-256+#endif+ };+/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */+/* flag unnecessarily. It leads to a song and dance at the end of strtod. */+#define Scale_Bit 0x10+#define n_bigtens 5+#else+#ifdef IBM+bigtens[] = { 1e16, 1e32, 1e64 };+static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 };+#define n_bigtens 3+#else+bigtens[] = { 1e16, 1e32 };+static CONST double tinytens[] = { 1e-16, 1e-32 };+#define n_bigtens 2+#endif+#endif++#undef Need_Hexdig+#ifdef INFNAN_CHECK+#ifndef No_Hex_NaN+#define Need_Hexdig+#endif+#endif++#ifndef Need_Hexdig+#ifndef NO_HEX_FP+#define Need_Hexdig+#endif+#endif++#ifdef Need_Hexdig /*{*/+static unsigned char hexdig[256];++ static void+htinit(unsigned char *h, unsigned char *s, int inc)+{+ int i, j;+ for(i = 0; (j = s[i]) !=0; i++)+ h[j] = i + inc;+ }++ static void+hexdig_init(void)+{+#define USC (unsigned char *)+ htinit(hexdig, USC "0123456789", 0x10);+ htinit(hexdig, USC "abcdef", 0x10 + 10);+ htinit(hexdig, USC "ABCDEF", 0x10 + 10);+ }+#endif /* } Need_Hexdig */++#ifdef INFNAN_CHECK++#ifndef NAN_WORD0+#define NAN_WORD0 0x7ff80000+#endif++#ifndef NAN_WORD1+#define NAN_WORD1 0+#endif++ static int+match+ (struct dtoa_context* C, const char **sp, const char *t)+{+ int c, d;+ CONST char *s = *sp;++ while((d = *t++)) {+ if ((c = *++s) >= 'A' && c <= 'Z')+ c += 'a' - 'A';+ if (c != d)+ return 0;+ }+ *sp = s + 1;+ return 1;+ }++#ifndef No_Hex_NaN+ static void+hexnan+ (struct dtoa_context* C, U *rvp, const char **sp)+{+ ULong c, x[2];+ CONST char *s;+ int c1, havedig, udx0, xshift;++ if (!hexdig['0'])+ hexdig_init();+ x[0] = x[1] = 0;+ havedig = xshift = 0;+ udx0 = 1;+ s = *sp;+ /* allow optional initial 0x or 0X */+ while((c = *(CONST unsigned char*)(s+1)) && c <= ' ')+ ++s;+ if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X'))+ s += 2;+ while((c = *(CONST unsigned char*)++s)) {+ if ((c1 = hexdig[c]))+ c = c1 & 0xf;+ else if (c <= ' ') {+ if (udx0 && havedig) {+ udx0 = 0;+ xshift = 1;+ }+ continue;+ }+#ifdef GDTOA_NON_PEDANTIC_NANCHECK+ else if (/*(*/ c == ')' && havedig) {+ *sp = s + 1;+ break;+ }+ else+ return; /* invalid form: don't change *sp */+#else+ else {+ do {+ if (/*(*/ c == ')') {+ *sp = s + 1;+ break;+ }+ } while((c = *++s));+ break;+ }+#endif+ havedig = 1;+ if (xshift) {+ xshift = 0;+ x[0] = x[1];+ x[1] = 0;+ }+ if (udx0)+ x[0] = (x[0] << 4) | (x[1] >> 28);+ x[1] = (x[1] << 4) | c;+ }+ if ((x[0] &= 0xfffff) || x[1]) {+ word0(rvp) = Exp_mask | x[0];+ word1(rvp) = x[1];+ }+ }+#endif /*No_Hex_NaN*/+#endif /* INFNAN_CHECK */++#ifdef Pack_32+#define ULbits 32+#define kshift 5+#define kmask 31+#else+#define ULbits 16+#define kshift 4+#define kmask 15+#endif++#if !defined(NO_HEX_FP) || defined(Honor_FLT_ROUNDS) /*{*/+ static Bigint *+increment(struct dtoa_context* C, Bigint *b)+{+ ULong *x, *xe;+ Bigint *b1;++ x = b->x;+ xe = x + b->wds;+ do {+ if (*x < (ULong)0xffffffffL) {+ ++*x;+ return b;+ }+ *x++ = 0;+ } while(x < xe);+ {+ if (b->wds >= b->maxwds) {+ b1 = Balloc(C, b->k+1);+ Bcopy(b1,b);+ Bfree(C, b);+ b = b1;+ }+ b->x[b->wds++] = 1;+ }+ return b;+ }++#endif /*}*/++#ifndef NO_HEX_FP /*{*/++ static void+rshift(struct dtoa_context* C, Bigint *b, int k)+{+ ULong *x, *x1, *xe, y;+ int n;++ x = x1 = b->x;+ n = k >> kshift;+ if (n < b->wds) {+ xe = x + b->wds;+ x += n;+ if (k &= kmask) {+ n = 32 - k;+ y = *x++ >> k;+ while(x < xe) {+ *x1++ = (y | (*x << n)) & 0xffffffff;+ y = *x++ >> k;+ }+ if ((*x1 = y) !=0)+ x1++;+ }+ else+ while(x < xe)+ *x1++ = *x++;+ }+ if ((b->wds = x1 - b->x) == 0)+ b->x[0] = 0;+ }++ static ULong+any_on(Bigint *b, int k)+{+ int n, nwds;+ ULong *x, *x0, x1, x2;++ x = b->x;+ nwds = b->wds;+ n = k >> kshift;+ if (n > nwds)+ n = nwds;+ else if (n < nwds && (k &= kmask)) {+ x1 = x2 = x[n];+ x1 >>= k;+ x1 <<= k;+ if (x1 != x2)+ return 1;+ }+ x0 = x;+ x += n;+ while(x > x0)+ if (*--x)+ return 1;+ return 0;+ }++enum { /* rounding values: same as FLT_ROUNDS */+ Round_zero = 0,+ Round_near = 1,+ Round_up = 2,+ Round_down = 3+ };++static void+gethex(struct dtoa_context* C, CONST char **sp, U *rvp, int rounding, int sign)+{+ Bigint *b;+ CONST unsigned char *decpt, *s0, *s, *s1;+ Long e, e1;+ ULong L, lostbits, *x;+ int big, denorm, esign, havedig, k, n, nbits, up, zret;+#ifdef IBM+ int j;+#endif+ enum {+#ifdef IEEE_Arith /*{{*/+ emax = 0x7fe - Bias - P + 1,+ emin = Emin - P + 1+#else /*}{*/+ emin = Emin - P,+#ifdef VAX+ emax = 0x7ff - Bias - P + 1+#endif+#ifdef IBM+ emax = 0x7f - Bias - P+#endif+#endif /*}}*/+ };+#ifdef USE_LOCALE+ int i;+#ifdef NO_LOCALE_CACHE+ const unsigned char *decimalpoint = (unsigned char*)+ localeconv()->decimal_point;+#else+ const unsigned char *decimalpoint;+ static unsigned char *decimalpoint_cache;+ if (!(s0 = decimalpoint_cache)) {+ s0 = (unsigned char*)localeconv()->decimal_point;+ if ((decimalpoint_cache = (unsigned char*)+ MALLOC(strlen((CONST char*)s0) + 1))) {+ strcpy((char*)decimalpoint_cache, (CONST char*)s0);+ s0 = decimalpoint_cache;+ }+ }+ decimalpoint = s0;+#endif+#endif++ if (!hexdig['0'])+ hexdig_init();+ havedig = 0;+ s0 = *(CONST unsigned char **)sp + 2;+ while(s0[havedig] == '0')+ havedig++;+ s0 += havedig;+ s = s0;+ decpt = 0;+ zret = 0;+ e = 0;+ if (hexdig[*s])+ havedig++;+ else {+ zret = 1;+#ifdef USE_LOCALE+ for(i = 0; decimalpoint[i]; ++i) {+ if (s[i] != decimalpoint[i])+ goto pcheck;+ }+ decpt = s += i;+#else+ if (*s != '.')+ goto pcheck;+ decpt = ++s;+#endif+ if (!hexdig[*s])+ goto pcheck;+ while(*s == '0')+ s++;+ if (hexdig[*s])+ zret = 0;+ havedig = 1;+ s0 = s;+ }+ while(hexdig[*s])+ s++;+#ifdef USE_LOCALE+ if (*s == *decimalpoint && !decpt) {+ for(i = 1; decimalpoint[i]; ++i) {+ if (s[i] != decimalpoint[i])+ goto pcheck;+ }+ decpt = s += i;+#else+ if (*s == '.' && !decpt) {+ decpt = ++s;+#endif+ while(hexdig[*s])+ s++;+ }/*}*/+ if (decpt)+ e = -(((Long)(s-decpt)) << 2);+ pcheck:+ s1 = s;+ big = esign = 0;+ switch(*s) {+ case 'p':+ case 'P':+ switch(*++s) {+ case '-':+ esign = 1;+ /* no break */+ case '+':+ s++;+ }+ if ((n = hexdig[*s]) == 0 || n > 0x19) {+ s = s1;+ break;+ }+ e1 = n - 0x10;+ while((n = hexdig[*++s]) !=0 && n <= 0x19) {+ if (e1 & 0xf8000000)+ big = 1;+ e1 = 10*e1 + n - 0x10;+ }+ if (esign)+ e1 = -e1;+ e += e1;+ }+ *sp = (char*)s;+ if (!havedig)+ *sp = (char*)s0 - 1;+ if (zret)+ goto retz1;+ if (big) {+ if (esign) {+#ifdef IEEE_Arith+ switch(rounding) {+ case Round_up:+ if (sign)+ break;+ goto ret_tiny;+ case Round_down:+ if (!sign)+ break;+ goto ret_tiny;+ }+#endif+ goto retz;+#ifdef IEEE_Arith+ ret_tiny:+#ifndef NO_ERRNO+ errno = ERANGE;+#endif+ word0(rvp) = 0;+ word1(rvp) = 1;+ return;+#endif /* IEEE_Arith */+ }+ switch(rounding) {+ case Round_near:+ goto ovfl1;+ case Round_up:+ if (!sign)+ goto ovfl1;+ goto ret_big;+ case Round_down:+ if (sign)+ goto ovfl1;+ goto ret_big;+ }+ ret_big:+ word0(rvp) = Big0;+ word1(rvp) = Big1;+ return;+ }+ n = s1 - s0 - 1;+ for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)+ k++;+ b = Balloc(C, k);+ x = b->x;+ n = 0;+ L = 0;+#ifdef USE_LOCALE+ for(i = 0; decimalpoint[i+1]; ++i);+#endif+ while(s1 > s0) {+#ifdef USE_LOCALE+ if (*--s1 == decimalpoint[i]) {+ s1 -= i;+ continue;+ }+#else+ if (*--s1 == '.')+ continue;+#endif+ if (n == ULbits) {+ *x++ = L;+ L = 0;+ n = 0;+ }+ L |= (hexdig[*s1] & 0x0f) << n;+ n += 4;+ }+ *x++ = L;+ b->wds = n = x - b->x;+ n = ULbits*n - hi0bits(C, L);+ nbits = Nbits;+ lostbits = 0;+ x = b->x;+ if (n > nbits) {+ n -= nbits;+ if (any_on(b,n)) {+ lostbits = 1;+ k = n - 1;+ if (x[k>>kshift] & 1 << (k & kmask)) {+ lostbits = 2;+ if (k > 0 && any_on(b,k))+ lostbits = 3;+ }+ }+ rshift(C, b, n);+ e += n;+ }+ else if (n < nbits) {+ n = nbits - n;+ b = lshift(C, b, n);+ e -= n;+ x = b->x;+ }+ if (e > Emax) {+ ovfl:+ Bfree(C, b);+ ovfl1:+#ifndef NO_ERRNO+ errno = ERANGE;+#endif+ word0(rvp) = Exp_mask;+ word1(rvp) = 0;+ return;+ }+ denorm = 0;+ if (e < emin) {+ denorm = 1;+ n = emin - e;+ if (n >= nbits) {+#ifdef IEEE_Arith /*{*/+ switch (rounding) {+ case Round_near:+ if (n == nbits && (n < 2 || any_on(b,n-1)))+ goto ret_tiny;+ break;+ case Round_up:+ if (!sign)+ goto ret_tiny;+ break;+ case Round_down:+ if (sign)+ goto ret_tiny;+ }+#endif /* } IEEE_Arith */+ Bfree(C, b);+ retz:+#ifndef NO_ERRNO+ errno = ERANGE;+#endif+ retz1:+ rvp->d = 0.;+ return;+ }+ k = n - 1;+ if (lostbits)+ lostbits = 1;+ else if (k > 0)+ lostbits = any_on(b,k);+ if (x[k>>kshift] & 1 << (k & kmask))+ lostbits |= 2;+ nbits -= n;+ rshift(C, b,n);+ e = emin;+ }+ if (lostbits) {+ up = 0;+ switch(rounding) {+ case Round_zero:+ break;+ case Round_near:+ if (lostbits & 2+ && (lostbits & 1) | (x[0] & 1))+ up = 1;+ break;+ case Round_up:+ up = 1 - sign;+ break;+ case Round_down:+ up = sign;+ }+ if (up) {+ k = b->wds;+ b = increment(C, b);+ x = b->x;+ if (denorm) {+#if 0+ if (nbits == Nbits - 1+ && x[nbits >> kshift] & 1 << (nbits & kmask))+ denorm = 0; /* not currently used */+#endif+ }+ else if (b->wds > k+ || ((n = nbits & kmask) !=0+ && hi0bits(C, x[k-1]) < 32-n)) {+ rshift(C, b,1);+ if (++e > Emax)+ goto ovfl;+ }+ }+ }+#ifdef IEEE_Arith+ if (denorm)+ word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0;+ else+ word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20);+ word1(rvp) = b->x[0];+#endif+#ifdef IBM+ if ((j = e & 3)) {+ k = b->x[0] & ((1 << j) - 1);+ rshift(C, b,j);+ if (k) {+ switch(rounding) {+ case Round_up:+ if (!sign)+ increment(b);+ break;+ case Round_down:+ if (sign)+ increment(b);+ break;+ case Round_near:+ j = 1 << (j-1);+ if (k & j && ((k & (j-1)) | lostbits))+ increment(b);+ }+ }+ }+ e >>= 2;+ word0(rvp) = b->x[1] | ((e + 65 + 13) << 24);+ word1(rvp) = b->x[0];+#endif+#ifdef VAX+ /* The next two lines ignore swap of low- and high-order 2 bytes. */+ /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */+ /* word1(rvp) = b->x[0]; */+ word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16);+ word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16);+#endif+ Bfree(C, b);+ }+#endif /*!NO_HEX_FP}*/++ static int+dshift(struct dtoa_context* C, Bigint *b, int p2)+{+ int rv = hi0bits(C, b->x[b->wds-1]) - 4;+ if (p2 > 0)+ rv -= p2;+ return rv & kmask;+ }++ static int+quorem+ (struct dtoa_context* C, Bigint *b, Bigint *S)+{+ int n;+ ULong *bx, *bxe, q, *sx, *sxe;+#ifdef ULLong+ ULLong borrow, carry, y, ys;+#else+ ULong borrow, carry, y, ys;+#ifdef Pack_32+ ULong si, z, zs;+#endif+#endif++ n = S->wds;+#ifdef DEBUG+ /*debug*/ if (b->wds > n)+ /*debug*/ Bug("oversize b in quorem");+#endif+ if (b->wds < n)+ return 0;+ sx = S->x;+ sxe = sx + --n;+ bx = b->x;+ bxe = bx + n;+ q = *bxe / (*sxe + 1); /* ensure q <= true quotient */+#ifdef DEBUG+#ifdef NO_STRTOD_BIGCOMP+ /*debug*/ if (q > 9)+#else+ /* An oversized q is possible when quorem is called from bigcomp and */+ /* the input is near, e.g., twice the smallest denormalized number. */+ /*debug*/ if (q > 15)+#endif+ /*debug*/ Bug("oversized quotient in quorem");+#endif+ if (q) {+ borrow = 0;+ carry = 0;+ do {+#ifdef ULLong+ ys = *sx++ * (ULLong)q + carry;+ carry = ys >> 32;+ y = *bx - (ys & FFFFFFFF) - borrow;+ borrow = y >> 32 & (ULong)1;+ *bx++ = y & FFFFFFFF;+#else+#ifdef Pack_32+ si = *sx++;+ ys = (si & 0xffff) * q + carry;+ zs = (si >> 16) * q + (ys >> 16);+ carry = zs >> 16;+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ z = (*bx >> 16) - (zs & 0xffff) - borrow;+ borrow = (z & 0x10000) >> 16;+ Storeinc(bx, z, y);+#else+ ys = *sx++ * q + carry;+ carry = ys >> 16;+ y = *bx - (ys & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ *bx++ = y & 0xffff;+#endif+#endif+ }+ while(sx <= sxe);+ if (!*bxe) {+ bx = b->x;+ while(--bxe > bx && !*bxe)+ --n;+ b->wds = n;+ }+ }+ if (cmp(C, b, S) >= 0) {+ q++;+ borrow = 0;+ carry = 0;+ bx = b->x;+ sx = S->x;+ do {+#ifdef ULLong+ ys = *sx++ + carry;+ carry = ys >> 32;+ y = *bx - (ys & FFFFFFFF) - borrow;+ borrow = y >> 32 & (ULong)1;+ *bx++ = y & FFFFFFFF;+#else+#ifdef Pack_32+ si = *sx++;+ ys = (si & 0xffff) + carry;+ zs = (si >> 16) + (ys >> 16);+ carry = zs >> 16;+ y = (*bx & 0xffff) - (ys & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ z = (*bx >> 16) - (zs & 0xffff) - borrow;+ borrow = (z & 0x10000) >> 16;+ Storeinc(bx, z, y);+#else+ ys = *sx++ + carry;+ carry = ys >> 16;+ y = *bx - (ys & 0xffff) - borrow;+ borrow = (y & 0x10000) >> 16;+ *bx++ = y & 0xffff;+#endif+#endif+ }+ while(sx <= sxe);+ bx = b->x;+ bxe = bx + n;+ if (!*bxe) {+ while(--bxe > bx && !*bxe)+ --n;+ b->wds = n;+ }+ }+ return q;+ }++#if defined(Avoid_Underflow) || !defined(NO_STRTOD_BIGCOMP) /*{*/+ static double+sulp+ (struct dtoa_context* C, U *x, BCinfo *bc)+{+ U u;+ double rv;+ int i;++ rv = ulp(C, x);+ if (!bc->scale || (i = 2*P + 1 - ((word0(x) & Exp_mask) >> Exp_shift)) <= 0)+ return rv; /* Is there an example where i <= 0 ? */+ word0(&u) = Exp_1 + (i << Exp_shift);+ word1(&u) = 0;+ return rv * u.d;+ }+#endif /*}*/++#ifndef NO_STRTOD_BIGCOMP+ static void+bigcomp+ (struct dtoa_context* C, U *rv, const char *s0, BCinfo *bc)+{+ Bigint *b, *d;+ int b2, bbits, d2, dd=0, dig, dsign, i, j, nd, nd0, p2, p5, speccase;++ dsign = bc->dsign;+ nd = bc->nd;+ nd0 = bc->nd0;+ p5 = nd + bc->e0 - 1;+ speccase = 0;+#ifndef Sudden_Underflow+ if (rv->d == 0.) { /* special case: value near underflow-to-zero */+ /* threshold was rounded to zero */+ b = i2b(C, 1);+ p2 = Emin - P + 1;+ bbits = 1;+#ifdef Avoid_Underflow+ word0(rv) = (P+2) << Exp_shift;+#else+ word1(rv) = 1;+#endif+ i = 0;+#ifdef Honor_FLT_ROUNDS+ if (bc->rounding == 1)+#endif+ {+ speccase = 1;+ --p2;+ dsign = 0;+ goto have_i;+ }+ }+ else+#endif+ b = d2b(C, rv, &p2, &bbits);+#ifdef Avoid_Underflow+ p2 -= bc->scale;+#endif+ /* floor(log2(rv)) == bbits - 1 + p2 */+ /* Check for denormal case. */+ i = P - bbits;+ if (i > (j = P - Emin - 1 + p2)) {+#ifdef Sudden_Underflow+ Bfree(C, b);+ b = i2b(C, 1);+ p2 = Emin;+ i = P - 1;+#ifdef Avoid_Underflow+ word0(rv) = (1 + bc->scale) << Exp_shift;+#else+ word0(rv) = Exp_msk1;+#endif+ word1(rv) = 0;+#else+ i = j;+#endif+ }+#ifdef Honor_FLT_ROUNDS+ if (bc->rounding != 1) {+ if (i > 0)+ b = lshift(C, b, i);+ if (dsign)+ b = increment(b);+ }+ else+#endif+ {+ b = lshift(C, b, ++i);+ b->x[0] |= 1;+ }+#ifndef Sudden_Underflow+ have_i:+#endif+ p2 -= p5 + i;+ d = i2b(C, 1);+ /* Arrange for convenient computation of quotients:+ * shift left if necessary so divisor has 4 leading 0 bits.+ */+ if (p5 > 0)+ d = pow5mult(C, d, p5);+ else if (p5 < 0)+ b = pow5mult(C, b, -p5);+ if (p2 > 0) {+ b2 = p2;+ d2 = 0;+ }+ else {+ b2 = 0;+ d2 = -p2;+ }+ i = dshift(C, d, d2);+ if ((b2 += i) > 0)+ b = lshift(C, b, b2);+ if ((d2 += i) > 0)+ d = lshift(C, d, d2);++ /* Now b/d = exactly half-way between the two floating-point values */+ /* on either side of the input string. Compute first digit of b/d. */++ if (!(dig = quorem(C, b,d))) {+ b = multadd(C, b, 10, 0); /* very unlikely */+ dig = quorem(C, b,d);+ }++ /* Compare b/d with s0 */++ for(i = 0; i < nd0; ) {+ if ((dd = s0[i++] - '0' - dig))+ goto ret;+ if (!b->x[0] && b->wds == 1) {+ if (i < nd)+ dd = 1;+ goto ret;+ }+ b = multadd(C, b, 10, 0);+ dig = quorem(C, b,d);+ }+ for(j = bc->dp1; i++ < nd;) {+ if ((dd = s0[j++] - '0' - dig))+ goto ret;+ if (!b->x[0] && b->wds == 1) {+ if (i < nd)+ dd = 1;+ goto ret;+ }+ b = multadd(C, b, 10, 0);+ dig = quorem(C, b,d);+ }+ if (dig > 0 || b->x[0] || b->wds > 1)+ dd = -1;+ ret:+ Bfree(C, b);+ Bfree(C, d);+#ifdef Honor_FLT_ROUNDS+ if (bc->rounding != 1) {+ if (dd < 0) {+ if (bc->rounding == 0) {+ if (!dsign)+ goto retlow1;+ }+ else if (dsign)+ goto rethi1;+ }+ else if (dd > 0) {+ if (bc->rounding == 0) {+ if (dsign)+ goto rethi1;+ goto ret1;+ }+ if (!dsign)+ goto rethi1;+ dval(rv) += 2.*sulp(C, rv,bc);+ }+ else {+ bc->inexact = 0;+ if (dsign)+ goto rethi1;+ }+ }+ else+#endif+ if (speccase) {+ if (dd <= 0)+ rv->d = 0.;+ }+ else if (dd < 0) {+ if (!dsign) /* does not happen for round-near */+retlow1:+ dval(rv) -= sulp(C, rv,bc);+ }+ else if (dd > 0) {+ if (dsign) {+ rethi1:+ dval(rv) += sulp(C, rv,bc);+ }+ }+ else {+ /* Exact half-way case: apply round-even rule. */+ if ((j = ((word0(rv) & Exp_mask) >> Exp_shift) - bc->scale) <= 0) {+ i = 1 - j;+ if (i <= 31) {+ if (word1(rv) & (0x1 << i))+ goto odd;+ }+ else if (word0(rv) & (0x1 << (i-32)))+ goto odd;+ }+ else if (word1(rv) & 1) {+ odd:+ if (dsign)+ goto rethi1;+ goto retlow1;+ }+ }++#ifdef Honor_FLT_ROUNDS+ ret1:+#endif+ return;+ }+#endif /* NO_STRTOD_BIGCOMP */++ double+jvp_strtod+ (struct dtoa_context* C, const char *s00, char **se)+{+ int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1;+ int esign, i, j, k, nd, nd0, nf, nz, nz0, nz1, sign;+ CONST char *s, *s0, *s1;+ double aadj, aadj1;+ Long L;+ U aadj2, adj, rv, rv0;+ ULong y, z;+ BCinfo bc;+ Bigint *bb=0, *bb1, *bd=0, *bd0, *bs=0, *delta=0;+#ifdef Avoid_Underflow+ ULong Lsb, Lsb1;+#endif+#ifdef SET_INEXACT+ int oldinexact;+#endif+#ifndef NO_STRTOD_BIGCOMP+ int req_bigcomp = 0;+#endif+#ifdef Honor_FLT_ROUNDS /*{*/+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */+ bc.rounding = Flt_Rounds;+#else /*}{*/+ bc.rounding = 1;+ switch(fegetround()) {+ case FE_TOWARDZERO: bc.rounding = 0; break;+ case FE_UPWARD: bc.rounding = 2; break;+ case FE_DOWNWARD: bc.rounding = 3;+ }+#endif /*}}*/+#endif /*}*/+#ifdef USE_LOCALE+ CONST char *s2;+#endif++ sign = nz0 = nz1 = nz = bc.dplen = bc.uflchk = 0;+ dval(&rv) = 0.;+ for(s = s00;;s++) switch(*s) {+ case '-':+ sign = 1;+ /* no break */+ case '+':+ if (*++s)+ goto break2;+ /* no break */+ case 0:+ goto ret0;+ case '\t':+ case '\n':+ case '\v':+ case '\f':+ case '\r':+ case ' ':+ continue;+ default:+ goto break2;+ }+ break2:+ if (*s == '0') {+#ifndef NO_HEX_FP /*{*/+ switch(s[1]) {+ case 'x':+ case 'X':+#ifdef Honor_FLT_ROUNDS+ gethex(C, &s, &rv, bc.rounding, sign);+#else+ gethex(C, &s, &rv, 1, sign);+#endif+ goto ret;+ }+#endif /*}*/+ nz0 = 1;+ while(*++s == '0') ;+ if (!*s)+ goto ret;+ }+ s0 = s;+ y = z = 0;+ for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++)+ if (nd < 9)+ y = 10*y + c - '0';+ else if (nd < 16)+ z = 10*z + c - '0';+ nd0 = nd;+ bc.dp0 = bc.dp1 = s - s0;+ for(s1 = s; s1 > s0 && *--s1 == '0'; )+ ++nz1;+#ifdef USE_LOCALE+ s1 = localeconv()->decimal_point;+ if (c == *s1) {+ c = '.';+ if (*++s1) {+ s2 = s;+ for(;;) {+ if (*++s2 != *s1) {+ c = 0;+ break;+ }+ if (!*++s1) {+ s = s2;+ break;+ }+ }+ }+ }+#endif+ if (c == '.') {+ c = *++s;+ bc.dp1 = s - s0;+ bc.dplen = bc.dp1 - bc.dp0;+ if (!nd) {+ for(; c == '0'; c = *++s)+ nz++;+ if (c > '0' && c <= '9') {+ bc.dp0 = s0 - s;+ bc.dp1 = bc.dp0 + bc.dplen;+ s0 = s;+ nf += nz;+ nz = 0;+ goto have_dig;+ }+ goto dig_done;+ }+ for(; c >= '0' && c <= '9'; c = *++s) {+ have_dig:+ nz++;+ if (c -= '0') {+ nf += nz;+ for(i = 1; i < nz; i++)+ if (nd++ < 9)+ y *= 10;+ else if (nd <= DBL_DIG + 1)+ z *= 10;+ if (nd++ < 9)+ y = 10*y + c;+ else if (nd <= DBL_DIG + 1)+ z = 10*z + c;+ nz = nz1 = 0;+ }+ }+ }+ dig_done:+ e = 0;+ if (c == 'e' || c == 'E') {+ if (!nd && !nz && !nz0) {+ goto ret0;+ }+ s00 = s;+ esign = 0;+ switch(c = *++s) {+ case '-':+ esign = 1;+ case '+':+ c = *++s;+ }+ if (c >= '0' && c <= '9') {+ while(c == '0')+ c = *++s;+ if (c > '0' && c <= '9') {+ L = c - '0';+ s1 = s;+ while((c = *++s) >= '0' && c <= '9')+ L = 10*L + c - '0';+ if (s - s1 > 8 || L > 19999)+ /* Avoid confusion from exponents+ * so large that e might overflow.+ */+ e = 19999; /* safe for 16 bit ints */+ else+ e = (int)L;+ if (esign)+ e = -e;+ }+ else+ e = 0;+ }+ else+ s = s00;+ }+ if (!nd) {+ if (!nz && !nz0) {+#ifdef INFNAN_CHECK+ /* Check for Nan and Infinity */+ if (!bc.dplen)+ switch(c) {+ case 'i':+ case 'I':+ if (match(C, &s,"nf")) {+ --s;+ if (!match(C, &s,"inity"))+ ++s;+ word0(&rv) = 0x7ff00000;+ word1(&rv) = 0;+ goto ret;+ }+ break;+ case 'n':+ case 'N':+ if (match(C, &s, "an")) {+ word0(&rv) = NAN_WORD0;+ word1(&rv) = NAN_WORD1;+#ifndef No_Hex_NaN+ if (*s == '(') /*)*/+ hexnan(C, &rv, &s);+#endif+ goto ret;+ }+ }+#endif /* INFNAN_CHECK */+ ret0:+ s = s00;+ sign = 0;+ }+ goto ret;+ }+ bc.e0 = e1 = e -= nf;++ /* Now we have nd0 digits, starting at s0, followed by a+ * decimal point, followed by nd-nd0 digits. The number we're+ * after is the integer represented by those digits times+ * 10**e */++ if (!nd0)+ nd0 = nd;+ k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1;+ dval(&rv) = y;+ if (k > 9) {+#ifdef SET_INEXACT+ if (k > DBL_DIG)+ oldinexact = get_inexact();+#endif+ dval(&rv) = tens[k - 9] * dval(&rv) + z;+ }+ bd0 = 0;+ if (nd <= DBL_DIG+#ifndef RND_PRODQUOT+#ifndef Honor_FLT_ROUNDS+ && Flt_Rounds == 1+#endif+#endif+ ) {+ if (!e)+ goto ret;+#ifndef ROUND_BIASED_without_Round_Up+ if (e > 0) {+ if (e <= Ten_pmax) {+#ifdef VAX+ goto vax_ovfl_check;+#else+#ifdef Honor_FLT_ROUNDS+ /* round correctly FLT_ROUNDS = 2 or 3 */+ if (sign) {+ rv.d = -rv.d;+ sign = 0;+ }+#endif+ /* rv = */ rounded_product(dval(&rv), tens[e]);+ goto ret;+#endif+ }+ i = DBL_DIG - nd;+ if (e <= Ten_pmax + i) {+ /* A fancier test would sometimes let us do+ * this for larger i values.+ */+#ifdef Honor_FLT_ROUNDS+ /* round correctly FLT_ROUNDS = 2 or 3 */+ if (sign) {+ rv.d = -rv.d;+ sign = 0;+ }+#endif+ e -= i;+ dval(&rv) *= tens[i];+#ifdef VAX+ /* VAX exponent range is so narrow we must+ * worry about overflow here...+ */+ vax_ovfl_check:+ word0(&rv) -= P*Exp_msk1;+ /* rv = */ rounded_product(dval(&rv), tens[e]);+ if ((word0(&rv) & Exp_mask)+ > Exp_msk1*(DBL_MAX_EXP+Bias-1-P))+ goto ovfl;+ word0(&rv) += P*Exp_msk1;+#else+ /* rv = */ rounded_product(dval(&rv), tens[e]);+#endif+ goto ret;+ }+ }+#ifndef Inaccurate_Divide+ else if (e >= -Ten_pmax) {+#ifdef Honor_FLT_ROUNDS+ /* round correctly FLT_ROUNDS = 2 or 3 */+ if (sign) {+ rv.d = -rv.d;+ sign = 0;+ }+#endif+ /* rv = */ rounded_quotient(dval(&rv), tens[-e]);+ goto ret;+ }+#endif+#endif /* ROUND_BIASED_without_Round_Up */+ }+ e1 += nd - k;++#ifdef IEEE_Arith+#ifdef SET_INEXACT+ bc.inexact = 1;+ if (k <= DBL_DIG)+ oldinexact = get_inexact();+#endif+#ifdef Avoid_Underflow+ bc.scale = 0;+#endif+#ifdef Honor_FLT_ROUNDS+ if (bc.rounding >= 2) {+ if (sign)+ bc.rounding = bc.rounding == 2 ? 0 : 2;+ else+ if (bc.rounding != 2)+ bc.rounding = 0;+ }+#endif+#endif /*IEEE_Arith*/++ /* Get starting approximation = rv * 10**e1 */++ if (e1 > 0) {+ if ((i = e1 & 15))+ dval(&rv) *= tens[i];+ if (e1 &= ~15) {+ if (e1 > DBL_MAX_10_EXP) {+ ovfl:+ /* Can't trust HUGE_VAL */+#ifdef IEEE_Arith+#ifdef Honor_FLT_ROUNDS+ switch(bc.rounding) {+ case 0: /* toward 0 */+ case 3: /* toward -infinity */+ word0(&rv) = Big0;+ word1(&rv) = Big1;+ break;+ default:+ word0(&rv) = Exp_mask;+ word1(&rv) = 0;+ }+#else /*Honor_FLT_ROUNDS*/+ word0(&rv) = Exp_mask;+ word1(&rv) = 0;+#endif /*Honor_FLT_ROUNDS*/+#ifdef SET_INEXACT+ /* set overflow bit */+ dval(&rv0) = 1e300;+ dval(&rv0) *= dval(&rv0);+#endif+#else /*IEEE_Arith*/+ word0(&rv) = Big0;+ word1(&rv) = Big1;+#endif /*IEEE_Arith*/+ range_err:+ if (bd0) {+ Bfree(C, bb);+ Bfree(C, bd);+ Bfree(C, bs);+ Bfree(C, bd0);+ Bfree(C, delta);+ }+#ifndef NO_ERRNO+ errno = ERANGE;+#endif+ goto ret;+ }+ e1 >>= 4;+ for(j = 0; e1 > 1; j++, e1 >>= 1)+ if (e1 & 1)+ dval(&rv) *= bigtens[j];+ /* The last multiplication could overflow. */+ word0(&rv) -= P*Exp_msk1;+ dval(&rv) *= bigtens[j];+ if ((z = word0(&rv) & Exp_mask)+ > Exp_msk1*(DBL_MAX_EXP+Bias-P))+ goto ovfl;+ if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) {+ /* set to largest number */+ /* (Can't trust DBL_MAX) */+ word0(&rv) = Big0;+ word1(&rv) = Big1;+ }+ else+ word0(&rv) += P*Exp_msk1;+ }+ }+ else if (e1 < 0) {+ e1 = -e1;+ if ((i = e1 & 15))+ dval(&rv) /= tens[i];+ if (e1 >>= 4) {+ if (e1 >= 1 << n_bigtens)+ goto undfl;+#ifdef Avoid_Underflow+ if (e1 & Scale_Bit)+ bc.scale = 2*P;+ for(j = 0; e1 > 0; j++, e1 >>= 1)+ if (e1 & 1)+ dval(&rv) *= tinytens[j];+ if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask)+ >> Exp_shift)) > 0) {+ /* scaled rv is denormal; clear j low bits */+ if (j >= 32) {+ if (j > 54)+ goto undfl;+ word1(&rv) = 0;+ if (j >= 53)+ word0(&rv) = (P+2)*Exp_msk1;+ else+ word0(&rv) &= 0xffffffff << (j-32);+ }+ else+ word1(&rv) &= 0xffffffff << j;+ }+#else+ for(j = 0; e1 > 1; j++, e1 >>= 1)+ if (e1 & 1)+ dval(&rv) *= tinytens[j];+ /* The last multiplication could underflow. */+ dval(&rv0) = dval(&rv);+ dval(&rv) *= tinytens[j];+ if (!dval(&rv)) {+ dval(&rv) = 2.*dval(&rv0);+ dval(&rv) *= tinytens[j];+#endif+ if (!dval(&rv)) {+ undfl:+ dval(&rv) = 0.;+ goto range_err;+ }+#ifndef Avoid_Underflow+ word0(&rv) = Tiny0;+ word1(&rv) = Tiny1;+ /* The refinement below will clean+ * this approximation up.+ */+ }+#endif+ }+ }++ /* Now the hard part -- adjusting rv to the correct value.*/++ /* Put digits into bd: true value = bd * 10^e */++ bc.nd = nd - nz1;+#ifndef NO_STRTOD_BIGCOMP+ bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */+ /* to silence an erroneous warning about bc.nd0 */+ /* possibly not being initialized. */+ if (nd > strtod_diglim) {+ /* ASSERT(strtod_diglim >= 18); 18 == one more than the */+ /* minimum number of decimal digits to distinguish double values */+ /* in IEEE arithmetic. */+ i = j = 18;+ if (i > nd0)+ j += bc.dplen;+ for(;;) {+ if (--j < bc.dp1 && j >= bc.dp0)+ j = bc.dp0 - 1;+ if (s0[j] != '0')+ break;+ --i;+ }+ e += nd - i;+ nd = i;+ if (nd0 > nd)+ nd0 = nd;+ if (nd < 9) { /* must recompute y */+ y = 0;+ for(i = 0; i < nd0; ++i)+ y = 10*y + s0[i] - '0';+ for(j = bc.dp1; i < nd; ++i)+ y = 10*y + s0[j++] - '0';+ }+ }+#endif+ bd0 = s2b(C, s0, nd0, nd, y, bc.dplen);++ for(;;) {+ bd = Balloc(C, bd0->k);+ Bcopy(bd, bd0);+ bb = d2b(C, &rv, &bbe, &bbbits); /* rv = bb * 2^bbe */+ bs = i2b(C, 1);++ if (e >= 0) {+ bb2 = bb5 = 0;+ bd2 = bd5 = e;+ }+ else {+ bb2 = bb5 = -e;+ bd2 = bd5 = 0;+ }+ if (bbe >= 0)+ bb2 += bbe;+ else+ bd2 -= bbe;+ bs2 = bb2;+#ifdef Honor_FLT_ROUNDS+ if (bc.rounding != 1)+ bs2++;+#endif+#ifdef Avoid_Underflow+ Lsb = LSB;+ Lsb1 = 0;+ j = bbe - bc.scale;+ i = j + bbbits - 1; /* logb(rv) */+ j = P + 1 - bbbits;+ if (i < Emin) { /* denormal */+ i = Emin - i;+ j -= i;+ if (i < 32)+ Lsb <<= i;+ else if (i < 52)+ Lsb1 = Lsb << (i-32);+ else+ Lsb1 = Exp_mask;+ }+#else /*Avoid_Underflow*/+#ifdef Sudden_Underflow+#ifdef IBM+ j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3);+#else+ j = P + 1 - bbbits;+#endif+#else /*Sudden_Underflow*/+ j = bbe;+ i = j + bbbits - 1; /* logb(rv) */+ if (i < Emin) /* denormal */+ j += P - Emin;+ else+ j = P + 1 - bbbits;+#endif /*Sudden_Underflow*/+#endif /*Avoid_Underflow*/+ bb2 += j;+ bd2 += j;+#ifdef Avoid_Underflow+ bd2 += bc.scale;+#endif+ i = bb2 < bd2 ? bb2 : bd2;+ if (i > bs2)+ i = bs2;+ if (i > 0) {+ bb2 -= i;+ bd2 -= i;+ bs2 -= i;+ }+ if (bb5 > 0) {+ bs = pow5mult(C, bs, bb5);+ bb1 = mult(C, bs, bb);+ Bfree(C, bb);+ bb = bb1;+ }+ if (bb2 > 0)+ bb = lshift(C, bb, bb2);+ if (bd5 > 0)+ bd = pow5mult(C, bd, bd5);+ if (bd2 > 0)+ bd = lshift(C, bd, bd2);+ if (bs2 > 0)+ bs = lshift(C, bs, bs2);+ delta = diff(C, bb, bd);+ bc.dsign = delta->sign;+ delta->sign = 0;+ i = cmp(C, delta, bs);+#ifndef NO_STRTOD_BIGCOMP /*{*/+ if (bc.nd > nd && i <= 0) {+ if (bc.dsign) {+ /* Must use bigcomp(C, ). */+ req_bigcomp = 1;+ break;+ }+#ifdef Honor_FLT_ROUNDS+ if (bc.rounding != 1) {+ if (i < 0) {+ req_bigcomp = 1;+ break;+ }+ }+ else+#endif+ i = -1; /* Discarded digits make delta smaller. */+ }+#endif /*}*/+#ifdef Honor_FLT_ROUNDS /*{*/+ if (bc.rounding != 1) {+ if (i < 0) {+ /* Error is less than an ulp */+ if (!delta->x[0] && delta->wds <= 1) {+ /* exact */+#ifdef SET_INEXACT+ bc.inexact = 0;+#endif+ break;+ }+ if (bc.rounding) {+ if (bc.dsign) {+ adj.d = 1.;+ goto apply_adj;+ }+ }+ else if (!bc.dsign) {+ adj.d = -1.;+ if (!word1(&rv)+ && !(word0(&rv) & Frac_mask)) {+ y = word0(&rv) & Exp_mask;+#ifdef Avoid_Underflow+ if (!bc.scale || y > 2*P*Exp_msk1)+#else+ if (y)+#endif+ {+ delta = lshift(C, delta,Log2P);+ if (cmp(C, delta, bs) <= 0)+ adj.d = -0.5;+ }+ }+ apply_adj:+#ifdef Avoid_Underflow /*{*/+ if (bc.scale && (y = word0(&rv) & Exp_mask)+ <= 2*P*Exp_msk1)+ word0(&adj) += (2*P+1)*Exp_msk1 - y;+#else+#ifdef Sudden_Underflow+ if ((word0(&rv) & Exp_mask) <=+ P*Exp_msk1) {+ word0(&rv) += P*Exp_msk1;+ dval(&rv) += adj.d*ulp(C, dval(&rv));+ word0(&rv) -= P*Exp_msk1;+ }+ else+#endif /*Sudden_Underflow*/+#endif /*Avoid_Underflow}*/+ dval(&rv) += adj.d*ulp(C, &rv);+ }+ break;+ }+ adj.d = ratio(C, delta, bs);+ if (adj.d < 1.)+ adj.d = 1.;+ if (adj.d <= 0x7ffffffe) {+ /* adj = rounding ? ceil(adj) : floor(adj); */+ y = adj.d;+ if (y != adj.d) {+ if (!((bc.rounding>>1) ^ bc.dsign))+ y++;+ adj.d = y;+ }+ }+#ifdef Avoid_Underflow /*{*/+ if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)+ word0(&adj) += (2*P+1)*Exp_msk1 - y;+#else+#ifdef Sudden_Underflow+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {+ word0(&rv) += P*Exp_msk1;+ adj.d *= ulp(C, dval(&rv));+ if (bc.dsign)+ dval(&rv) += adj.d;+ else+ dval(&rv) -= adj.d;+ word0(&rv) -= P*Exp_msk1;+ goto cont;+ }+#endif /*Sudden_Underflow*/+#endif /*Avoid_Underflow}*/+ adj.d *= ulp(C, &rv);+ if (bc.dsign) {+ if (word0(&rv) == Big0 && word1(&rv) == Big1)+ goto ovfl;+ dval(&rv) += adj.d;+ }+ else+ dval(&rv) -= adj.d;+ goto cont;+ }+#endif /*}Honor_FLT_ROUNDS*/++ if (i < 0) {+ /* Error is less than half an ulp -- check for+ * special case of mantissa a power of two.+ */+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask+#ifdef IEEE_Arith /*{*/+#ifdef Avoid_Underflow+ || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1+#else+ || (word0(&rv) & Exp_mask) <= Exp_msk1+#endif+#endif /*}*/+ ) {+#ifdef SET_INEXACT+ if (!delta->x[0] && delta->wds <= 1)+ bc.inexact = 0;+#endif+ break;+ }+ if (!delta->x[0] && delta->wds <= 1) {+ /* exact result */+#ifdef SET_INEXACT+ bc.inexact = 0;+#endif+ break;+ }+ delta = lshift(C, delta,Log2P);+ if (cmp(C, delta, bs) > 0)+ goto drop_down;+ break;+ }+ if (i == 0) {+ /* exactly half-way between */+ if (bc.dsign) {+ if ((word0(&rv) & Bndry_mask1) == Bndry_mask1+ && word1(&rv) == (+#ifdef Avoid_Underflow+ (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1)+ ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) :+#endif+ 0xffffffff)) {+ /*boundary case -- increment exponent*/+ if (word0(&rv) == Big0 && word1(&rv) == Big1)+ goto ovfl;+ word0(&rv) = (word0(&rv) & Exp_mask)+ + Exp_msk1+#ifdef IBM+ | Exp_msk1 >> 4+#endif+ ;+ word1(&rv) = 0;+#ifdef Avoid_Underflow+ bc.dsign = 0;+#endif+ break;+ }+ }+ else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) {+ drop_down:+ /* boundary case -- decrement exponent */+#ifdef Sudden_Underflow /*{{*/+ L = word0(&rv) & Exp_mask;+#ifdef IBM+ if (L < Exp_msk1)+#else+#ifdef Avoid_Underflow+ if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1))+#else+ if (L <= Exp_msk1)+#endif /*Avoid_Underflow*/+#endif /*IBM*/+ {+ if (bc.nd >nd) {+ bc.uflchk = 1;+ break;+ }+ goto undfl;+ }+ L -= Exp_msk1;+#else /*Sudden_Underflow}{*/+#ifdef Avoid_Underflow+ if (bc.scale) {+ L = word0(&rv) & Exp_mask;+ if (L <= (2*P+1)*Exp_msk1) {+ if (L > (P+2)*Exp_msk1)+ /* round even ==> */+ /* accept rv */+ break;+ /* rv = smallest denormal */+ if (bc.nd >nd) {+ bc.uflchk = 1;+ break;+ }+ goto undfl;+ }+ }+#endif /*Avoid_Underflow*/+ L = (word0(&rv) & Exp_mask) - Exp_msk1;+#endif /*Sudden_Underflow}}*/+ word0(&rv) = L | Bndry_mask1;+ word1(&rv) = 0xffffffff;+#ifdef IBM+ goto cont;+#else+#ifndef NO_STRTOD_BIGCOMP+ if (bc.nd > nd)+ goto cont;+#endif+ break;+#endif+ }+#ifndef ROUND_BIASED+#ifdef Avoid_Underflow+ if (Lsb1) {+ if (!(word0(&rv) & Lsb1))+ break;+ }+ else if (!(word1(&rv) & Lsb))+ break;+#else+ if (!(word1(&rv) & LSB))+ break;+#endif+#endif+ if (bc.dsign)+#ifdef Avoid_Underflow+ dval(&rv) += sulp(C, &rv, &bc);+#else+ dval(&rv) += ulp(C, &rv);+#endif+#ifndef ROUND_BIASED+ else {+#ifdef Avoid_Underflow+ dval(&rv) -= sulp(C, &rv, &bc);+#else+ dval(&rv) -= ulp(C, &rv);+#endif+#ifndef Sudden_Underflow+ if (!dval(&rv)) {+ if (bc.nd >nd) {+ bc.uflchk = 1;+ break;+ }+ goto undfl;+ }+#endif+ }+#ifdef Avoid_Underflow+ bc.dsign = 1 - bc.dsign;+#endif+#endif+ break;+ }+ if ((aadj = ratio(C, delta, bs)) <= 2.) {+ if (bc.dsign)+ aadj = aadj1 = 1.;+ else if (word1(&rv) || word0(&rv) & Bndry_mask) {+#ifndef Sudden_Underflow+ if (word1(&rv) == Tiny1 && !word0(&rv)) {+ if (bc.nd >nd) {+ bc.uflchk = 1;+ break;+ }+ goto undfl;+ }+#endif+ aadj = 1.;+ aadj1 = -1.;+ }+ else {+ /* special case -- power of FLT_RADIX to be */+ /* rounded down... */++ if (aadj < 2./FLT_RADIX)+ aadj = 1./FLT_RADIX;+ else+ aadj *= 0.5;+ aadj1 = -aadj;+ }+ }+ else {+ aadj *= 0.5;+ aadj1 = bc.dsign ? aadj : -aadj;+#ifdef Check_FLT_ROUNDS+ switch(bc.rounding) {+ case 2: /* towards +infinity */+ aadj1 -= 0.5;+ break;+ case 0: /* towards 0 */+ case 3: /* towards -infinity */+ aadj1 += 0.5;+ }+#else+ if (Flt_Rounds == 0)+ aadj1 += 0.5;+#endif /*Check_FLT_ROUNDS*/+ }+ y = word0(&rv) & Exp_mask;++ /* Check for overflow */++ if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) {+ dval(&rv0) = dval(&rv);+ word0(&rv) -= P*Exp_msk1;+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+ if ((word0(&rv) & Exp_mask) >=+ Exp_msk1*(DBL_MAX_EXP+Bias-P)) {+ if (word0(&rv0) == Big0 && word1(&rv0) == Big1)+ goto ovfl;+ word0(&rv) = Big0;+ word1(&rv) = Big1;+ goto cont;+ }+ else+ word0(&rv) += P*Exp_msk1;+ }+ else {+#ifdef Avoid_Underflow+ if (bc.scale && y <= 2*P*Exp_msk1) {+ if (aadj <= 0x7fffffff) {+ if ((z = aadj) <= 0)+ z = 1;+ aadj = z;+ aadj1 = bc.dsign ? aadj : -aadj;+ }+ dval(&aadj2) = aadj1;+ word0(&aadj2) += (2*P+1)*Exp_msk1 - y;+ aadj1 = dval(&aadj2);+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+ if (rv.d == 0.)+#ifdef NO_STRTOD_BIGCOMP+ goto undfl;+#else+ {+ if (bc.nd > nd)+ bc.dsign = 1;+ break;+ }+#endif+ }+ else {+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+ }+#else+#ifdef Sudden_Underflow+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) {+ dval(&rv0) = dval(&rv);+ word0(&rv) += P*Exp_msk1;+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+#ifdef IBM+ if ((word0(&rv) & Exp_mask) < P*Exp_msk1)+#else+ if ((word0(&rv) & Exp_mask) <= P*Exp_msk1)+#endif+ {+ if (word0(&rv0) == Tiny0+ && word1(&rv0) == Tiny1) {+ if (bc.nd >nd) {+ bc.uflchk = 1;+ break;+ }+ goto undfl;+ }+ word0(&rv) = Tiny0;+ word1(&rv) = Tiny1;+ goto cont;+ }+ else+ word0(&rv) -= P*Exp_msk1;+ }+ else {+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+ }+#else /*Sudden_Underflow*/+ /* Compute adj so that the IEEE rounding rules will+ * correctly round rv + adj in some half-way cases.+ * If rv * ulp(C, rv) is denormalized (i.e.,+ * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid+ * trouble from bits lost to denormalization;+ * example: 1.2e-307 .+ */+ if (y <= (P-1)*Exp_msk1 && aadj > 1.) {+ aadj1 = (double)(int)(aadj + 0.5);+ if (!bc.dsign)+ aadj1 = -aadj1;+ }+ adj.d = aadj1 * ulp(C, &rv);+ dval(&rv) += adj.d;+#endif /*Sudden_Underflow*/+#endif /*Avoid_Underflow*/+ }+ z = word0(&rv) & Exp_mask;+#ifndef SET_INEXACT+ if (bc.nd == nd) {+#ifdef Avoid_Underflow+ if (!bc.scale)+#endif+ if (y == z) {+ /* Can we stop now? */+ L = (Long)aadj;+ aadj -= L;+ /* The tolerances below are conservative. */+ if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) {+ if (aadj < .4999999 || aadj > .5000001)+ break;+ }+ else if (aadj < .4999999/FLT_RADIX)+ break;+ }+ }+#endif+ cont:+ Bfree(C, bb);+ Bfree(C, bd);+ Bfree(C, bs);+ Bfree(C, delta);+ }+ Bfree(C, bb);+ Bfree(C, bd);+ Bfree(C, bs);+ Bfree(C, bd0);+ Bfree(C, delta);+#ifndef NO_STRTOD_BIGCOMP+ if (req_bigcomp) {+ bd0 = 0;+ bc.e0 += nz1;+ bigcomp(C, &rv, s0, &bc);+ y = word0(&rv) & Exp_mask;+ if (y == Exp_mask)+ goto ovfl;+ if (y == 0 && rv.d == 0.)+ goto undfl;+ }+#endif+#ifdef SET_INEXACT+ if (bc.inexact) {+ if (!oldinexact) {+ word0(&rv0) = Exp_1 + (70 << Exp_shift);+ word1(&rv0) = 0;+ dval(&rv0) += 1.;+ }+ }+ else if (!oldinexact)+ clear_inexact();+#endif+#ifdef Avoid_Underflow+ if (bc.scale) {+ word0(&rv0) = Exp_1 - 2*P*Exp_msk1;+ word1(&rv0) = 0;+ dval(&rv) *= dval(&rv0);+#ifndef NO_ERRNO+ /* try to avoid the bug of testing an 8087 register value */+#ifdef IEEE_Arith+ if (!(word0(&rv) & Exp_mask))+#else+ if (word0(&rv) == 0 && word1(&rv) == 0)+#endif+ errno = ERANGE;+#endif+ }+#endif /* Avoid_Underflow */+#ifdef SET_INEXACT+ if (bc.inexact && !(word0(&rv) & Exp_mask)) {+ /* set underflow bit */+ dval(&rv0) = 1e-300;+ dval(&rv0) *= dval(&rv0);+ }+#endif+ ret:+ if (se)+ *se = (char *)s;+ return sign ? -dval(&rv) : dval(&rv);+ }++ static char *+rv_alloc(struct dtoa_context* C, int i)+{+ int j, k, *r;++ j = sizeof(ULong);+ for(k = 0;+ (int)(sizeof(Bigint) - sizeof(ULong) - sizeof(int)) + j <= i;+ j <<= 1)+ k++;+ r = (int*)Balloc(C, k);+ *r = k;+ return+ (char *)(r+1);+ }++ static char *+nrv_alloc(struct dtoa_context* C, const char *s, char **rve, int n)+{+ char *rv, *t;++ t = rv = rv_alloc(C, n);+ while((*t = *s++)) t++;+ if (rve)+ *rve = t;+ return rv;+ }++/* freedtoa(s) must be used to free values s returned by dtoa+ * when MULTIPLE_THREADS is #defined. It should be used in all cases,+ * but for consistency with earlier versions of dtoa, it is optional+ * when MULTIPLE_THREADS is not defined.+ */++ void+jvp_freedtoa(struct dtoa_context* C, char *s)+{+ Bigint *b = (Bigint *)((int *)s - 1);+ b->maxwds = 1 << (b->k = *(int*)b);+ Bfree(C, b);+ }++/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string.+ *+ * Inspired by "How to Print Floating-Point Numbers Accurately" by+ * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126].+ *+ * Modifications:+ * 1. Rather than iterating, we use a simple numeric overestimate+ * to determine k = floor(log10(d)). We scale relevant+ * quantities using O(log2(k)) rather than O(k) multiplications.+ * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't+ * try to generate digits strictly left to right. Instead, we+ * compute with fewer bits and propagate the carry if necessary+ * when rounding the final digit up. This is often faster.+ * 3. Under the assumption that input will be rounded nearest,+ * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22.+ * That is, we allow equality in stopping tests when the+ * round-nearest rule will give the same floating-point value+ * as would satisfaction of the stopping test with strict+ * inequality.+ * 4. We remove common factors of powers of 2 from relevant+ * quantities.+ * 5. When converting floating-point integers less than 1e16,+ * we use floating-point arithmetic rather than resorting+ * to multiple-precision integers.+ * 6. When asked to produce fewer than 15 digits, we first try+ * to get by with floating-point arithmetic; we resort to+ * multiple-precision integer arithmetic only if we cannot+ * guarantee that the floating-point calculation has given+ * the correctly rounded result. For k requested digits and+ * "uniformly" distributed input, the probability is+ * something like 10^(k-15) that we must resort to the Long+ * calculation.+ */++ char *+jvp_dtoa+ (struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve)+{+ /* Arguments ndigits, decpt, sign are similar to those+ of ecvt and fcvt; trailing zeros are suppressed from+ the returned string. If not null, *rve is set to point+ to the end of the return value. If d is +-Infinity or NaN,+ then *decpt is set to 9999.++ mode:+ 0 ==> shortest string that yields d when read in+ and rounded to nearest.+ 1 ==> like 0, but with Steele & White stopping rule;+ e.g. with IEEE P754 arithmetic , mode 0 gives+ 1e23 whereas mode 1 gives 9.999999999999999e22.+ 2 ==> max(1,ndigits) significant digits. This gives a+ return value similar to that of ecvt, except+ that trailing zeros are suppressed.+ 3 ==> through ndigits past the decimal point. This+ gives a return value similar to that from fcvt,+ except that trailing zeros are suppressed, and+ ndigits can be negative.+ 4,5 ==> similar to 2 and 3, respectively, but (in+ round-nearest mode) with the tests of mode 0 to+ possibly return a shorter string that rounds to d.+ With IEEE arithmetic and compilation with+ -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same+ as modes 2 and 3 when FLT_ROUNDS != 1.+ 6-9 ==> Debugging modes similar to mode - 4: don't try+ fast floating-point estimate (if applicable).++ Values of mode other than 0-9 are treated as mode 0.++ Sufficient space is allocated to the return value+ to hold the suppressed trailing zeros.+ */++ int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1,+ j, j1=0, k, k0, k_check, leftright, m2, m5, s2, s5,+ spec_case, try_quick;+ Long L;+#ifndef Sudden_Underflow+ int denorm;+ ULong x;+#endif+ Bigint *b, *b1, *delta, *mlo, *mhi, *S;+ U d2, eps, u;+ double ds;+ char *s, *s0;+#ifndef No_leftright+#ifdef IEEE_Arith+ U eps1;+#endif+#endif+#ifdef SET_INEXACT+ int inexact, oldinexact;+#endif+#ifdef Honor_FLT_ROUNDS /*{*/+ int Rounding;+#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */+ Rounding = Flt_Rounds;+#else /*}{*/+ Rounding = 1;+ switch(fegetround()) {+ case FE_TOWARDZERO: Rounding = 0; break;+ case FE_UPWARD: Rounding = 2; break;+ case FE_DOWNWARD: Rounding = 3;+ }+#endif /*}}*/+#endif /*}*/+++ u.d = dd;+ if (word0(&u) & Sign_bit) {+ /* set sign for everything, including 0's and NaNs */+ *sign = 1;+ word0(&u) &= ~Sign_bit; /* clear sign bit */+ }+ else+ *sign = 0;++#if defined(IEEE_Arith) + defined(VAX)+#ifdef IEEE_Arith+ if ((word0(&u) & Exp_mask) == Exp_mask)+#else+ if (word0(&u) == 0x8000)+#endif+ {+ /* Infinity or NaN */+ *decpt = 9999;+#ifdef IEEE_Arith+ if (!word1(&u) && !(word0(&u) & 0xfffff))+ return nrv_alloc(C, "Infinity", rve, 8);+#endif+ return nrv_alloc(C, "NaN", rve, 3);+ }+#endif+#ifdef IBM+ dval(&u) += 0; /* normalize */+#endif+ if (!dval(&u)) {+ *decpt = 1;+ return nrv_alloc(C, "0", rve, 1);+ }++#ifdef SET_INEXACT+ try_quick = oldinexact = get_inexact();+ inexact = 1;+#endif+#ifdef Honor_FLT_ROUNDS+ if (Rounding >= 2) {+ if (*sign)+ Rounding = Rounding == 2 ? 0 : 2;+ else+ if (Rounding != 2)+ Rounding = 0;+ }+#endif++ b = d2b(C, &u, &be, &bbits);+#ifdef Sudden_Underflow+ i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1));+#else+ if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) {+#endif+ dval(&d2) = dval(&u);+ word0(&d2) &= Frac_mask1;+ word0(&d2) |= Exp_11;+#ifdef IBM+ if (j = 11 - hi0bits(C, word0(&d2) & Frac_mask))+ dval(&d2) /= 1 << j;+#endif++ /* log(x) ~=~ log(1.5) + (x-1.5)/1.5+ * log10(x) = log(x) / log(10)+ * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10))+ * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2)+ *+ * This suggests computing an approximation k to log10(d) by+ *+ * k = (i - Bias)*0.301029995663981+ * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 );+ *+ * We want k to be too large rather than too small.+ * The error in the first-order Taylor series approximation+ * is in our favor, so we just round up the constant enough+ * to compensate for any error in the multiplication of+ * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077,+ * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14,+ * adding 1e-13 to the constant term more than suffices.+ * Hence we adjust the constant term to 0.1760912590558.+ * (We could get a more accurate k by invoking log10,+ * but this is probably not worthwhile.)+ */++ i -= Bias;+#ifdef IBM+ i <<= 2;+ i += j;+#endif+#ifndef Sudden_Underflow+ denorm = 0;+ }+ else {+ /* d is denormalized */++ i = bbits + be + (Bias + (P-1) - 1);+ x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32)+ : word1(&u) << (32 - i);+ dval(&d2) = x;+ word0(&d2) -= 31*Exp_msk1; /* adjust exponent */+ i -= (Bias + (P-1) - 1) + 1;+ denorm = 1;+ }+#endif+ ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981;+ k = (int)ds;+ if (ds < 0. && ds != k)+ k--; /* want k = floor(ds) */+ k_check = 1;+ if (k >= 0 && k <= Ten_pmax) {+ if (dval(&u) < tens[k])+ k--;+ k_check = 0;+ }+ j = bbits - i - 1;+ if (j >= 0) {+ b2 = 0;+ s2 = j;+ }+ else {+ b2 = -j;+ s2 = 0;+ }+ if (k >= 0) {+ b5 = 0;+ s5 = k;+ s2 += k;+ }+ else {+ b2 -= k;+ b5 = -k;+ s5 = 0;+ }+ if (mode < 0 || mode > 9)+ mode = 0;++#ifndef SET_INEXACT+#ifdef Check_FLT_ROUNDS+ try_quick = Rounding == 1;+#else+ try_quick = 1;+#endif+#endif /*SET_INEXACT*/++ if (mode > 5) {+ mode -= 4;+ try_quick = 0;+ }+ leftright = 1;+ ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */+ /* silence erroneous "gcc -Wall" warning. */+ switch(mode) {+ case 0:+ case 1:+ i = 18;+ ndigits = 0;+ break;+ case 2:+ leftright = 0;+ /* no break */+ case 4:+ if (ndigits <= 0)+ ndigits = 1;+ ilim = ilim1 = i = ndigits;+ break;+ case 3:+ leftright = 0;+ /* no break */+ case 5:+ i = ndigits + k + 1;+ ilim = i;+ ilim1 = i - 1;+ if (i <= 0)+ i = 1;+ }+ s = s0 = rv_alloc(C, i);++#ifdef Honor_FLT_ROUNDS+ if (mode > 1 && Rounding != 1)+ leftright = 0;+#endif++ if (ilim >= 0 && ilim <= Quick_max && try_quick) {++ /* Try to get by with floating-point arithmetic. */++ i = 0;+ dval(&d2) = dval(&u);+ k0 = k;+ ilim0 = ilim;+ ieps = 2; /* conservative */+ if (k > 0) {+ ds = tens[k&0xf];+ j = k >> 4;+ if (j & Bletch) {+ /* prevent overflows */+ j &= Bletch - 1;+ dval(&u) /= bigtens[n_bigtens-1];+ ieps++;+ }+ for(; j; j >>= 1, i++)+ if (j & 1) {+ ieps++;+ ds *= bigtens[i];+ }+ dval(&u) /= ds;+ }+ else if ((j1 = -k)) {+ dval(&u) *= tens[j1 & 0xf];+ for(j = j1 >> 4; j; j >>= 1, i++)+ if (j & 1) {+ ieps++;+ dval(&u) *= bigtens[i];+ }+ }+ if (k_check && dval(&u) < 1. && ilim > 0) {+ if (ilim1 <= 0)+ goto fast_failed;+ ilim = ilim1;+ k--;+ dval(&u) *= 10.;+ ieps++;+ }+ dval(&eps) = ieps*dval(&u) + 7.;+ word0(&eps) -= (P-1)*Exp_msk1;+ if (ilim == 0) {+ S = mhi = 0;+ dval(&u) -= 5.;+ if (dval(&u) > dval(&eps))+ goto one_digit;+ if (dval(&u) < -dval(&eps))+ goto no_digits;+ goto fast_failed;+ }+#ifndef No_leftright+ if (leftright) {+ /* Use Steele & White method of only+ * generating digits needed.+ */+ dval(&eps) = 0.5/tens[ilim-1] - dval(&eps);+#ifdef IEEE_Arith+ if (k0 < 0 && j1 >= 307) {+ eps1.d = 1.01e256; /* 1.01 allows roundoff in the next few lines */+ word0(&eps1) -= Exp_msk1 * (Bias+P-1);+ dval(&eps1) *= tens[j1 & 0xf];+ for(i = 0, j = (j1-256) >> 4; j; j >>= 1, i++)+ if (j & 1)+ dval(&eps1) *= bigtens[i];+ if (eps.d < eps1.d)+ eps.d = eps1.d;+ }+#endif+ for(i = 0;;) {+ L = dval(&u);+ dval(&u) -= L;+ *s++ = '0' + (int)L;+ if (1. - dval(&u) < dval(&eps))+ goto bump_up;+ if (dval(&u) < dval(&eps))+ goto ret1;+ if (++i >= ilim)+ break;+ dval(&eps) *= 10.;+ dval(&u) *= 10.;+ }+ }+ else {+#endif+ /* Generate ilim digits, then fix them up. */+ dval(&eps) *= tens[ilim-1];+ for(i = 1;; i++, dval(&u) *= 10.) {+ L = (Long)(dval(&u));+ if (!(dval(&u) -= L))+ ilim = i;+ *s++ = '0' + (int)L;+ if (i == ilim) {+ if (dval(&u) > 0.5 + dval(&eps))+ goto bump_up;+ else if (dval(&u) < 0.5 - dval(&eps)) {+ while(*--s == '0');+ s++;+ goto ret1;+ }+ break;+ }+ }+#ifndef No_leftright+ }+#endif+ fast_failed:+ s = s0;+ dval(&u) = dval(&d2);+ k = k0;+ ilim = ilim0;+ }++ /* Do we have a "small" integer? */++ if (be >= 0 && k <= Int_max) {+ /* Yes. */+ ds = tens[k];+ if (ndigits < 0 && ilim <= 0) {+ S = mhi = 0;+ if (ilim < 0 || dval(&u) <= 5*ds)+ goto no_digits;+ goto one_digit;+ }+ for(i = 1;; i++, dval(&u) *= 10.) {+ L = (Long)(dval(&u) / ds);+ dval(&u) -= L*ds;+#ifdef Check_FLT_ROUNDS+ /* If FLT_ROUNDS == 2, L will usually be high by 1 */+ if (dval(&u) < 0) {+ L--;+ dval(&u) += ds;+ }+#endif+ *s++ = '0' + (int)L;+ if (!dval(&u)) {+#ifdef SET_INEXACT+ inexact = 0;+#endif+ break;+ }+ if (i == ilim) {+#ifdef Honor_FLT_ROUNDS+ if (mode > 1)+ switch(Rounding) {+ case 0: goto ret1;+ case 2: goto bump_up;+ }+#endif+ dval(&u) += dval(&u);+#ifdef ROUND_BIASED+ if (dval(&u) >= ds)+#else+ if (dval(&u) > ds || (dval(&u) == ds && L & 1))+#endif+ {+ bump_up:+ while(*--s == '9')+ if (s == s0) {+ k++;+ *s = '0';+ break;+ }+ ++*s++;+ }+ break;+ }+ }+ goto ret1;+ }++ m2 = b2;+ m5 = b5;+ mhi = mlo = 0;+ if (leftright) {+ i =+#ifndef Sudden_Underflow+ denorm ? be + (Bias + (P-1) - 1 + 1) :+#endif+#ifdef IBM+ 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3);+#else+ 1 + P - bbits;+#endif+ b2 += i;+ s2 += i;+ mhi = i2b(C, 1);+ }+ if (m2 > 0 && s2 > 0) {+ i = m2 < s2 ? m2 : s2;+ b2 -= i;+ m2 -= i;+ s2 -= i;+ }+ if (b5 > 0) {+ if (leftright) {+ if (m5 > 0) {+ mhi = pow5mult(C, mhi, m5);+ b1 = mult(C, mhi, b);+ Bfree(C, b);+ b = b1;+ }+ if ((j = b5 - m5))+ b = pow5mult(C, b, j);+ }+ else+ b = pow5mult(C, b, b5);+ }+ S = i2b(C, 1);+ if (s5 > 0)+ S = pow5mult(C, S, s5);++ /* Check for special case that d is a normalized power of 2. */++ spec_case = 0;+ if ((mode < 2 || leftright)+#ifdef Honor_FLT_ROUNDS+ && Rounding == 1+#endif+ ) {+ if (!word1(&u) && !(word0(&u) & Bndry_mask)+#ifndef Sudden_Underflow+ && word0(&u) & (Exp_mask & ~Exp_msk1)+#endif+ ) {+ /* The special case */+ b2 += Log2P;+ s2 += Log2P;+ spec_case = 1;+ }+ }++ /* Arrange for convenient computation of quotients:+ * shift left if necessary so divisor has 4 leading 0 bits.+ *+ * Perhaps we should just compute leading 28 bits of S once+ * and for all and pass them and a shift to quorem, so it+ * can do shifts and ors to compute the numerator for q.+ */+ i = dshift(C, S, s2);+ b2 += i;+ m2 += i;+ s2 += i;+ if (b2 > 0)+ b = lshift(C, b, b2);+ if (s2 > 0)+ S = lshift(C, S, s2);+ if (k_check) {+ if (cmp(C, b,S) < 0) {+ k--;+ b = multadd(C, b, 10, 0); /* we botched the k estimate */+ if (leftright)+ mhi = multadd(C, mhi, 10, 0);+ ilim = ilim1;+ }+ }+ if (ilim <= 0 && (mode == 3 || mode == 5)) {+ if (ilim < 0 || cmp(C, b,S = multadd(C, S,5,0)) <= 0) {+ /* no digits, fcvt style */+ no_digits:+ k = -1 - ndigits;+ goto ret;+ }+ one_digit:+ *s++ = '1';+ k++;+ goto ret;+ }+ if (leftright) {+ if (m2 > 0)+ mhi = lshift(C, mhi, m2);++ /* Compute mlo -- check for special case+ * that d is a normalized power of 2.+ */++ mlo = mhi;+ if (spec_case) {+ mhi = Balloc(C, mhi->k);+ Bcopy(mhi, mlo);+ mhi = lshift(C, mhi, Log2P);+ }++ for(i = 1;;i++) {+ dig = quorem(C, b,S) + '0';+ /* Do we yet have the shortest decimal string+ * that will round to d?+ */+ j = cmp(C, b, mlo);+ delta = diff(C, S, mhi);+ j1 = delta->sign ? 1 : cmp(C, b, delta);+ Bfree(C, delta);+#ifndef ROUND_BIASED+ if (j1 == 0 && mode != 1 && !(word1(&u) & 1)+#ifdef Honor_FLT_ROUNDS+ && Rounding >= 1+#endif+ ) {+ if (dig == '9')+ goto round_9_up;+ if (j > 0)+ dig++;+#ifdef SET_INEXACT+ else if (!b->x[0] && b->wds <= 1)+ inexact = 0;+#endif+ *s++ = dig;+ goto ret;+ }+#endif+ if (j < 0 || (j == 0 && mode != 1+#ifndef ROUND_BIASED+ && !(word1(&u) & 1)+#endif+ )) {+ if (!b->x[0] && b->wds <= 1) {+#ifdef SET_INEXACT+ inexact = 0;+#endif+ goto accept_dig;+ }+#ifdef Honor_FLT_ROUNDS+ if (mode > 1)+ switch(Rounding) {+ case 0: goto accept_dig;+ case 2: goto keep_dig;+ }+#endif /*Honor_FLT_ROUNDS*/+ if (j1 > 0) {+ b = lshift(C, b, 1);+ j1 = cmp(C, b, S);+#ifdef ROUND_BIASED+ if (j1 >= 0 /*)*/+#else+ if ((j1 > 0 || (j1 == 0 && dig & 1))+#endif+ && dig++ == '9')+ goto round_9_up;+ }+ accept_dig:+ *s++ = dig;+ goto ret;+ }+ if (j1 > 0) {+#ifdef Honor_FLT_ROUNDS+ if (!Rounding)+ goto accept_dig;+#endif+ if (dig == '9') { /* possible if i == 1 */+ round_9_up:+ *s++ = '9';+ goto roundoff;+ }+ *s++ = dig + 1;+ goto ret;+ }+#ifdef Honor_FLT_ROUNDS+ keep_dig:+#endif+ *s++ = dig;+ if (i == ilim)+ break;+ b = multadd(C, b, 10, 0);+ if (mlo == mhi)+ mlo = mhi = multadd(C, mhi, 10, 0);+ else {+ mlo = multadd(C, mlo, 10, 0);+ mhi = multadd(C, mhi, 10, 0);+ }+ }+ }+ else+ for(i = 1;; i++) {+ *s++ = dig = quorem(C, b,S) + '0';+ if (!b->x[0] && b->wds <= 1) {+#ifdef SET_INEXACT+ inexact = 0;+#endif+ goto ret;+ }+ if (i >= ilim)+ break;+ b = multadd(C, b, 10, 0);+ }++ /* Round off last digit */++#ifdef Honor_FLT_ROUNDS+ switch(Rounding) {+ case 0: goto trimzeros;+ case 2: goto roundoff;+ }+#endif+ b = lshift(C, b, 1);+ j = cmp(C, b, S);+#ifdef ROUND_BIASED+ if (j >= 0)+#else+ if (j > 0 || (j == 0 && dig & 1))+#endif+ {+ roundoff:+ while(*--s == '9')+ if (s == s0) {+ k++;+ *s++ = '1';+ goto ret;+ }+ ++*s++;+ }+ else {+#ifdef Honor_FLT_ROUNDS+ trimzeros:+#endif+ while(*--s == '0');+ s++;+ }+ ret:+ Bfree(C, S);+ if (mhi) {+ if (mlo && mlo != mhi)+ Bfree(C, mlo);+ Bfree(C, mhi);+ }+ ret1:+#ifdef SET_INEXACT+ if (inexact) {+ if (!oldinexact) {+ word0(&u) = Exp_1 + (70 << Exp_shift);+ word1(&u) = 0;+ dval(&u) += 1.;+ }+ }+ else if (!oldinexact)+ clear_inexact();+#endif+ Bfree(C, b);+ *s = 0;+ *decpt = k + 1;+ if (rve)+ *rve = s;+ return s0;+ }+#ifdef __cplusplus+}+#endif+++++++++/****************************************************************+ *+ * The author of this software is David M. Gay.+ *+ * Copyright (c) 1991, 1996 by Lucent Technologies.+ *+ * Permission to use, copy, modify, and distribute this software for any+ * purpose without fee is hereby granted, provided that this entire notice+ * is included in all copies of any software which is or includes a copy+ * or modification of this software and in all copies of the supporting+ * documentation for such software.+ *+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED+ * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY+ * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.+ *+ ***************************************************************/++/* g_fmt(buf,x) stores the closest decimal approximation to x in buf;+ * it suffices to declare buf+ * char buf[32];+ */++ char *+ jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x)+{+ register int i, k;+ register char *s;+ int decpt, j, sign;+ char *b0, *s0, *se;++ b0 = b;+#ifdef IGNORE_ZERO_SIGN+ if (!x) {+ *b++ = '0';+ *b = 0;+ goto done;+ }+#endif+ s = s0 = jvp_dtoa(C, x, 0, 0, &decpt, &sign, &se);+ if (sign)+ *b++ = '-';+ if (decpt == 9999) /* Infinity or Nan */ {+ while((*b++ = *s++));+ goto done0;+ }+ if (decpt <= -4 || decpt > se - s + 15) {+ *b++ = *s++;+ if (*s) {+ *b++ = '.';+ while((*b = *s++))+ b++;+ }+ *b++ = 'e';+ /* sprintf(b, "%+.2d", decpt - 1); */+ if (--decpt < 0) {+ *b++ = '-';+ decpt = -decpt;+ }+ else+ *b++ = '+';+ for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10);+ for(;;) {+ i = decpt / k;+ *b++ = i + '0';+ if (--j <= 0)+ break;+ decpt -= i*k;+ decpt *= 10;+ }+ *b = 0;+ }+ else if (decpt <= 0) {+ *b++ = '0';+ *b++ = '.';+ for(; decpt < 0; decpt++)+ *b++ = '0';+ while((*b++ = *s++));+ }+ else {+ while((*b = *s++)) {+ b++;+ if (--decpt == 0 && *s)+ *b++ = '.';+ }+ for(; decpt > 0; decpt--)+ *b++ = '0';+ *b = 0;+ }+ done0:+ jvp_freedtoa(C, s0);+ goto done;+ done:+ return b0;+ }
@@ -0,0 +1,22 @@+#ifndef JV_DTOA_H+#define JV_DTOA_H+#define Kmax 7++struct Bigint;+struct dtoa_context {+ struct Bigint *freelist[Kmax+1];+ struct Bigint *p5s;+};++void jvp_dtoa_context_init(struct dtoa_context* ctx);+void jvp_dtoa_context_free(struct dtoa_context* ctx);++double jvp_strtod(struct dtoa_context* C, const char* s, char** se);+++char* jvp_dtoa(struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve);+void jvp_freedtoa(struct dtoa_context* C, char *s);++#define JVP_DTOA_FMT_MAX_LEN 64+char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);+#endif
@@ -0,0 +1,49 @@++#include <errno.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include "jv.h"++jv jv_load_file(const char* filename, int raw) {+ FILE* file = fopen(filename, "r");+ struct jv_parser* parser;+ jv data;+ if (!file) {+ return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",+ filename,+ strerror(errno)));+ }+ if (raw) {+ data = jv_string("");+ } else {+ data = jv_array();+ parser = jv_parser_new(0);+ }+ while (!feof(file) && !ferror(file)) {+ char buf[4096];+ size_t n = fread(buf, 1, sizeof(buf), file);+ if (raw) {+ data = jv_string_concat(data, jv_string_sized(buf, (int)n));+ } else {+ jv_parser_set_buf(parser, buf, n, !feof(file));+ jv value;+ while (jv_is_valid((value = jv_parser_next(parser))))+ data = jv_array_append(data, value);+ if (jv_invalid_has_msg(jv_copy(value))) {+ jv_free(data);+ data = value;+ break;+ }+ }+ }+ if (!raw)+ jv_parser_free(parser);+ int badread = ferror(file);+ if (fclose(file) != 0 || badread) {+ jv_free(data);+ return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",+ filename));+ }+ return data;+}
@@ -0,0 +1,852 @@+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>+#include "jv.h"+#include "jv_dtoa.h"+#include "jv_unicode.h"+#include "jv_alloc.h"+#include "jv_dtoa.h"++typedef const char* presult;++#define TRY(x) do {presult msg__ = (x); if (msg__) return msg__; } while(0)+#ifdef __GNUC__+#define pfunc __attribute__((warn_unused_result)) presult+#else+#define pfunc presult+#endif++enum last_seen {+ JV_LAST_NONE = 0,+ JV_LAST_OPEN_ARRAY = '[',+ JV_LAST_OPEN_OBJECT = '{',+ JV_LAST_COLON = ':',+ JV_LAST_COMMA = ',',+ JV_LAST_VALUE = 'V',+};++struct jv_parser {+ const char* curr_buf;+ int curr_buf_length;+ int curr_buf_pos;+ int curr_buf_is_partial;+ int eof;+ unsigned bom_strip_position;++ int flags;++ jv* stack; // parser+ int stackpos; // parser+ int stacklen; // both (optimization; it's really pathlen for streaming)+ jv path; // streamer+ enum last_seen last_seen; // streamer+ jv output; // streamer+ jv next; // both++ char* tokenbuf;+ int tokenpos;+ int tokenlen;++ int line, column;++ struct dtoa_context dtoa;++ enum {+ JV_PARSER_NORMAL,+ JV_PARSER_STRING,+ JV_PARSER_STRING_ESCAPE,+ JV_PARSER_WAITING_FOR_RS // parse error, waiting for RS+ } st;+ unsigned int last_ch_was_ws:1;+};+++static void parser_init(struct jv_parser* p, int flags) {+ p->flags = flags;+ if ((p->flags & JV_PARSE_STREAMING)) {+ p->path = jv_array();+ } else {+ p->path = jv_invalid();+ p->flags &= ~(JV_PARSE_STREAM_ERRORS);+ }+ p->stack = 0;+ p->stacklen = p->stackpos = 0;+ p->last_seen = JV_LAST_NONE;+ p->output = jv_invalid();+ p->next = jv_invalid();+ p->tokenbuf = 0;+ p->tokenlen = p->tokenpos = 0;+ if ((p->flags & JV_PARSE_SEQ))+ p->st = JV_PARSER_WAITING_FOR_RS;+ else+ p->st = JV_PARSER_NORMAL;+ p->eof = 0;+ p->curr_buf = 0;+ p->curr_buf_length = p->curr_buf_pos = p->curr_buf_is_partial = 0;+ p->bom_strip_position = 0;+ p->last_ch_was_ws = 0;+ p->line = 1;+ p->column = 0;+ jvp_dtoa_context_init(&p->dtoa);+}++static void parser_reset(struct jv_parser* p) {+ if ((p->flags & JV_PARSE_STREAMING)) {+ jv_free(p->path);+ p->path = jv_array();+ p->stacklen = 0;+ }+ p->last_seen = JV_LAST_NONE;+ jv_free(p->output);+ p->output = jv_invalid();+ jv_free(p->next);+ p->next = jv_invalid();+ for (int i=0; i<p->stackpos; i++)+ jv_free(p->stack[i]);+ p->stackpos = 0;+ p->tokenpos = 0;+ p->st = JV_PARSER_NORMAL;+}++static void parser_free(struct jv_parser* p) {+ parser_reset(p);+ jv_free(p->path);+ jv_free(p->output);+ jv_mem_free(p->stack);+ jv_mem_free(p->tokenbuf);+ jvp_dtoa_context_free(&p->dtoa);+}++static pfunc value(struct jv_parser* p, jv val) {+ if ((p->flags & JV_PARSE_STREAMING)) {+ if (jv_is_valid(p->next) || p->last_seen == JV_LAST_VALUE)+ return "Expected separator between values";+ if (p->stacklen > 0)+ p->last_seen = JV_LAST_VALUE;+ else+ p->last_seen = JV_LAST_NONE;+ } else {+ if (jv_is_valid(p->next)) return "Expected separator between values";+ }+ jv_free(p->next);+ p->next = val;+ return 0;+}++static void push(struct jv_parser* p, jv v) {+ assert(p->stackpos <= p->stacklen);+ if (p->stackpos == p->stacklen) {+ p->stacklen = p->stacklen * 2 + 10;+ p->stack = jv_mem_realloc(p->stack, p->stacklen * sizeof(jv));+ }+ assert(p->stackpos < p->stacklen);+ p->stack[p->stackpos++] = v;+}++static pfunc parse_token(struct jv_parser* p, char ch) {+ switch (ch) {+ case '[':+ if (jv_is_valid(p->next)) return "Expected separator between values";+ push(p, jv_array());+ break;++ case '{':+ if (jv_is_valid(p->next)) return "Expected separator between values";+ push(p, jv_object());+ break;++ case ':':+ if (!jv_is_valid(p->next))+ return "Expected string key before ':'";+ if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)+ return "':' not as part of an object";+ if (jv_get_kind(p->next) != JV_KIND_STRING)+ return "Object keys must be strings";+ push(p, p->next);+ p->next = jv_invalid();+ break;++ case ',':+ if (!jv_is_valid(p->next))+ return "Expected value before ','";+ if (p->stackpos == 0)+ return "',' not as part of an object or array";+ if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_ARRAY) {+ p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);+ p->next = jv_invalid();+ } else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {+ assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);+ p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],+ p->stack[p->stackpos-1], p->next);+ p->stackpos--;+ p->next = jv_invalid();+ } else {+ // this case hits on input like {"a", "b"}+ return "Objects must consist of key:value pairs";+ }+ break;++ case ']':+ if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_ARRAY)+ return "Unmatched ']'";+ if (jv_is_valid(p->next)) {+ p->stack[p->stackpos-1] = jv_array_append(p->stack[p->stackpos-1], p->next);+ p->next = jv_invalid();+ } else {+ if (jv_array_length(jv_copy(p->stack[p->stackpos-1])) != 0) {+ // this case hits on input like [1,2,3,]+ return "Expected another array element";+ }+ }+ jv_free(p->next);+ p->next = p->stack[--p->stackpos];+ break;++ case '}':+ if (p->stackpos == 0)+ return "Unmatched '}'";+ if (jv_is_valid(p->next)) {+ if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)+ return "Objects must consist of key:value pairs";+ assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);+ p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],+ p->stack[p->stackpos-1], p->next);+ p->stackpos--;+ p->next = jv_invalid();+ } else {+ if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)+ return "Unmatched '}'";+ if (jv_object_length(jv_copy(p->stack[p->stackpos-1])) != 0)+ return "Expected another key-value pair";+ }+ jv_free(p->next);+ p->next = p->stack[--p->stackpos];+ break;+ }+ return 0;+}++static pfunc stream_token(struct jv_parser* p, char ch) {+ jv_kind k;+ jv last;++ switch (ch) {+ case '[':+ if (jv_is_valid(p->next))+ return "Expected a separator between values";+ p->path = jv_array_append(p->path, jv_number(0)); // push+ p->last_seen = JV_LAST_OPEN_ARRAY;+ p->stacklen++;+ break;++ case '{':+ if (p->last_seen == JV_LAST_VALUE)+ return "Expected a separator between values";+ // Push object key: null, since we don't know it yet+ p->path = jv_array_append(p->path, jv_null()); // push+ p->last_seen = JV_LAST_OPEN_OBJECT;+ p->stacklen++;+ break;++ case ':':+ if (p->stacklen == 0 || jv_get_kind(jv_array_get(jv_copy(p->path), p->stacklen - 1)) == JV_KIND_NUMBER)+ return "':' not as part of an object";+ if (!jv_is_valid(p->next) || p->last_seen == JV_LAST_NONE)+ return "Expected string key before ':'";+ if (jv_get_kind(p->next) != JV_KIND_STRING)+ return "Object keys must be strings";+ if (p->last_seen != JV_LAST_VALUE)+ return "':' should follow a key";+ p->last_seen = JV_LAST_COLON;+ p->path = jv_array_set(p->path, p->stacklen - 1, p->next);+ p->next = jv_invalid();+ break;++ case ',':+ if (p->last_seen != JV_LAST_VALUE)+ return "Expected value before ','";+ if (p->stacklen == 0)+ return "',' not as part of an object or array";+ last = jv_array_get(jv_copy(p->path), p->stacklen - 1);+ k = jv_get_kind(last);+ if (k == JV_KIND_NUMBER) {+ int idx = jv_number_value(last);++ if (jv_is_valid(p->next)) {+ p->output = JV_ARRAY(jv_copy(p->path), p->next);+ p->next = jv_invalid();+ }+ p->path = jv_array_set(p->path, p->stacklen - 1, jv_number(idx + 1));+ p->last_seen = JV_LAST_COMMA;+ } else if (k == JV_KIND_STRING) {+ if (jv_is_valid(p->next)) {+ p->output = JV_ARRAY(jv_copy(p->path), p->next);+ p->next = jv_invalid();+ }+ p->path = jv_array_set(p->path, p->stacklen - 1, jv_true()); // ready for another name:value pair+ p->last_seen = JV_LAST_COMMA;+ } else {+ assert(k == JV_KIND_NULL);+ // this case hits on input like {,}+ // make sure to handle input like {"a", "b"} and {"a":, ...}+ jv_free(last);+ return "Objects must consist of key:value pairs";+ }+ jv_free(last);+ break;++ case ']':+ if (p->stacklen == 0)+ return "Unmatched ']' at the top-level";+ if (p->last_seen == JV_LAST_COMMA)+ return "Expected another array element";+ if (p->last_seen == JV_LAST_OPEN_ARRAY)+ assert(!jv_is_valid(p->next));++ last = jv_array_get(jv_copy(p->path), p->stacklen - 1);+ k = jv_get_kind(last);+ jv_free(last);++ if (k != JV_KIND_NUMBER)+ return "Unmatched ']' in the middle of an object";+ if (jv_is_valid(p->next)) {+ p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());+ p->next = jv_invalid();+ } else if (p->last_seen != JV_LAST_OPEN_ARRAY) {+ p->output = JV_ARRAY(jv_copy(p->path));+ }++ p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop+ //assert(!jv_is_valid(p->next));+ jv_free(p->next);+ p->next = jv_invalid();++ if (p->last_seen == JV_LAST_OPEN_ARRAY)+ p->output = JV_ARRAY(jv_copy(p->path), jv_array()); // Empty arrays are leaves++ if (p->stacklen == 0)+ p->last_seen = JV_LAST_NONE;+ else+ p->last_seen = JV_LAST_VALUE;+ break;++ case '}':+ if (p->stacklen == 0)+ return "Unmatched '}' at the top-level";+ if (p->last_seen == JV_LAST_COMMA)+ return "Expected another key:value pair";+ if (p->last_seen == JV_LAST_OPEN_OBJECT)+ assert(!jv_is_valid(p->next));++ last = jv_array_get(jv_copy(p->path), p->stacklen - 1);+ k = jv_get_kind(last);+ jv_free(last);+ if (k == JV_KIND_NUMBER)+ return "Unmatched '}' in the middle of an array";++ if (jv_is_valid(p->next)) {+ if (k != JV_KIND_STRING)+ return "Objects must consist of key:value pairs";+ p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());+ p->next = jv_invalid();+ } else {+ // Perhaps {"a":[]}+ if (p->last_seen == JV_LAST_COLON)+ // Looks like {"a":}+ return "Missing value in key:value pair";+ if (p->last_seen == JV_LAST_COMMA)+ // Looks like {"a":0,}+ return "Expected another key-value pair";+ if (p->last_seen == JV_LAST_OPEN_ARRAY)+ return "Unmatched '}' in the middle of an array";+ if (p->last_seen != JV_LAST_VALUE && p->last_seen != JV_LAST_OPEN_OBJECT)+ return "Unmatched '}'";+ if (p->last_seen != JV_LAST_OPEN_OBJECT)+ p->output = JV_ARRAY(jv_copy(p->path));+ }+ p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop+ jv_free(p->next);+ p->next = jv_invalid();++ if (p->last_seen == JV_LAST_OPEN_OBJECT)+ p->output = JV_ARRAY(jv_copy(p->path), jv_object()); // Empty arrays are leaves++ if (p->stacklen == 0)+ p->last_seen = JV_LAST_NONE;+ else+ p->last_seen = JV_LAST_VALUE;+ break;+ }+ return 0;+}++static void tokenadd(struct jv_parser* p, char c) {+ assert(p->tokenpos <= p->tokenlen);+ if (p->tokenpos == p->tokenlen) {+ p->tokenlen = p->tokenlen*2 + 256;+ p->tokenbuf = jv_mem_realloc(p->tokenbuf, p->tokenlen);+ }+ assert(p->tokenpos < p->tokenlen);+ p->tokenbuf[p->tokenpos++] = c;+}++static int unhex4(char* hex) {+ int r = 0;+ for (int i=0; i<4; i++) {+ char c = *hex++;+ int n;+ if ('0' <= c && c <= '9') n = c - '0';+ else if ('a' <= c && c <= 'f') n = c - 'a' + 10;+ else if ('A' <= c && c <= 'F') n = c - 'A' + 10;+ else return -1;+ r <<= 4;+ r |= n;+ }+ return r;+}++static pfunc found_string(struct jv_parser* p) {+ char* in = p->tokenbuf;+ char* out = p->tokenbuf;+ char* end = p->tokenbuf + p->tokenpos;++ while (in < end) {+ char c = *in++;+ if (c == '\\') {+ if (in >= end)+ return "Expected escape character at end of string";+ c = *in++;+ switch (c) {+ case '\\':+ case '"':+ case '/': *out++ = c; break;+ case 'b': *out++ = '\b'; break;+ case 'f': *out++ = '\f'; break;+ case 't': *out++ = '\t'; break;+ case 'n': *out++ = '\n'; break;+ case 'r': *out++ = '\r'; break;++ case 'u':+ /* ahh, the complicated case */+ if (in + 4 > end)+ return "Invalid \\uXXXX escape";+ int hexvalue = unhex4(in);+ if (hexvalue < 0)+ return "Invalid characters in \\uXXXX escape";+ unsigned long codepoint = (unsigned long)hexvalue;+ in += 4;+ if (0xD800 <= codepoint && codepoint <= 0xDBFF) {+ /* who thought UTF-16 surrogate pairs were a good idea? */+ if (in + 6 > end || in[0] != '\\' || in[1] != 'u')+ return "Invalid \\uXXXX\\uXXXX surrogate pair escape";+ unsigned long surrogate = unhex4(in+2);+ if (!(0xDC00 <= surrogate && surrogate <= 0xDFFF))+ return "Invalid \\uXXXX\\uXXXX surrogate pair escape";+ in += 6;+ codepoint = 0x10000 + (((codepoint - 0xD800) << 10)+ |(surrogate - 0xDC00));+ }+ if (codepoint > 0x10FFFF)+ codepoint = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER+ out += jvp_utf8_encode(codepoint, out);+ break;++ default:+ return "Invalid escape";+ }+ } else {+ if (c > 0 && c < 0x001f)+ return "Invalid string: control characters from U+0000 through U+001F must be escaped";+ *out++ = c;+ }+ }+ TRY(value(p, jv_string_sized(p->tokenbuf, out - p->tokenbuf)));+ p->tokenpos = 0;+ return 0;+}++static pfunc check_literal(struct jv_parser* p) {+ if (p->tokenpos == 0) return 0;++ const char* pattern = 0;+ int plen;+ jv v;+ switch (p->tokenbuf[0]) {+ case 't': pattern = "true"; plen = 4; v = jv_true(); break;+ case 'f': pattern = "false"; plen = 5; v = jv_false(); break;+ case 'n': pattern = "null"; plen = 4; v = jv_null(); break;+ }+ if (pattern) {+ if (p->tokenpos != plen) return "Invalid literal";+ for (int i=0; i<plen; i++)+ if (p->tokenbuf[i] != pattern[i])+ return "Invalid literal";+ TRY(value(p, v));+ } else {+ // FIXME: better parser+ p->tokenbuf[p->tokenpos] = 0; // FIXME: invalid+ char* end = 0;+ double d = jvp_strtod(&p->dtoa, p->tokenbuf, &end);+ if (end == 0 || *end != 0)+ return "Invalid numeric literal";+ TRY(value(p, jv_number(d)));+ }+ p->tokenpos = 0;+ return 0;+}++typedef enum {+ LITERAL,+ WHITESPACE,+ STRUCTURE,+ QUOTE,+ INVALID+} chclass;++static chclass classify(char c) {+ switch (c) {+ case ' ':+ case '\t':+ case '\r':+ case '\n':+ return WHITESPACE;+ case '"':+ return QUOTE;+ case '[':+ case ',':+ case ']':+ case '{':+ case ':':+ case '}':+ return STRUCTURE;+ default:+ return LITERAL;+ }+}+++static const presult OK = "output produced";++static int parse_check_done(struct jv_parser* p, jv* out) {+ if (p->stackpos == 0 && jv_is_valid(p->next)) {+ *out = p->next;+ p->next = jv_invalid();+ return 1;+ } else {+ return 0;+ }+}++static int stream_check_done(struct jv_parser* p, jv* out) {+ if (p->stacklen == 0 && jv_is_valid(p->next)) {+ *out = JV_ARRAY(jv_copy(p->path),p->next);+ p->next = jv_invalid();+ return 1;+ } else if (jv_is_valid(p->output)) {+ if (jv_array_length(jv_copy(p->output)) > 2) {+ // At end of an array or object, necessitating one more output by+ // which to indicate this+ *out = jv_array_slice(jv_copy(p->output), 0, 2);+ p->output = jv_array_slice(p->output, 0, 1); // arrange one more output+ } else {+ // No further processing needed+ *out = p->output;+ p->output = jv_invalid();+ }+ return 1;+ } else {+ return 0;+ }+}++static int parse_check_truncation(struct jv_parser* p) {+ return ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && (p->stackpos > 0 || p->tokenpos > 0 || jv_get_kind(p->next) == JV_KIND_NUMBER));+}++static int stream_check_truncation(struct jv_parser* p) {+ jv_kind k = jv_get_kind(p->next);+ return (p->stacklen > 0 || k == JV_KIND_NUMBER || k == JV_KIND_TRUE || k == JV_KIND_FALSE || k == JV_KIND_NULL);+}++static int parse_is_top_num(struct jv_parser* p) {+ return (p->stackpos == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);+}++static int stream_is_top_num(struct jv_parser* p) {+ return (p->stacklen == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);+}++#define check_done(p, o) \+ (((p)->flags & JV_PARSE_STREAMING) ? stream_check_done((p), (o)) : parse_check_done((p), (o)))++#define token(p, ch) \+ (((p)->flags & JV_PARSE_STREAMING) ? stream_token((p), (ch)) : parse_token((p), (ch)))++#define check_truncation(p) \+ (((p)->flags & JV_PARSE_STREAMING) ? stream_check_truncation((p)) : parse_check_truncation((p)))++#define is_top_num(p) \+ (((p)->flags & JV_PARSE_STREAMING) ? stream_is_top_num((p)) : parse_is_top_num((p)))++static pfunc scan(struct jv_parser* p, char ch, jv* out) {+ p->column++;+ if (ch == '\n') {+ p->line++;+ p->column = 0;+ }+ if (ch == '\036' /* ASCII RS; see draft-ietf-json-sequence-07 */) {+ if (check_truncation(p)) {+ if (check_literal(p) == 0 && is_top_num(p))+ return "Potentially truncated top-level numeric value";+ return "Truncated value";+ }+ TRY(check_literal(p));+ if (p->st == JV_PARSER_NORMAL && check_done(p, out))+ return OK;+ // shouldn't happen?+ assert(!jv_is_valid(*out));+ parser_reset(p);+ jv_free(*out);+ *out = jv_invalid();+ return OK;+ }+ presult answer = 0;+ p->last_ch_was_ws = 0;+ if (p->st == JV_PARSER_NORMAL) {+ chclass cls = classify(ch);+ if (cls == WHITESPACE)+ p->last_ch_was_ws = 1;+ if (cls != LITERAL) {+ TRY(check_literal(p));+ if (check_done(p, out)) answer = OK;+ }+ switch (cls) {+ case LITERAL:+ tokenadd(p, ch);+ break;+ case WHITESPACE:+ break;+ case QUOTE:+ p->st = JV_PARSER_STRING;+ break;+ case STRUCTURE:+ TRY(token(p, ch));+ break;+ case INVALID:+ return "Invalid character";+ }+ if (check_done(p, out)) answer = OK;+ } else {+ if (ch == '"' && p->st == JV_PARSER_STRING) {+ TRY(found_string(p));+ p->st = JV_PARSER_NORMAL;+ if (check_done(p, out)) answer = OK;+ } else {+ tokenadd(p, ch);+ if (ch == '\\' && p->st == JV_PARSER_STRING) {+ p->st = JV_PARSER_STRING_ESCAPE;+ } else {+ p->st = JV_PARSER_STRING;+ }+ }+ }+ return answer;+}++struct jv_parser* jv_parser_new(int flags) {+ struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));+ parser_init(p, flags);+ p->flags = flags;+ return p;+}++void jv_parser_free(struct jv_parser* p) {+ parser_free(p);+ jv_mem_free(p);+}++static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};++int jv_parser_remaining(struct jv_parser* p) {+ if (p->curr_buf == 0)+ return 0;+ return (p->curr_buf_length - p->curr_buf_pos);+}++void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {+ assert((p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length)+ && "previous buffer not exhausted");+ while (length > 0 && p->bom_strip_position < sizeof(UTF8_BOM)) {+ if ((unsigned char)*buf == UTF8_BOM[p->bom_strip_position]) {+ // matched a BOM character+ buf++;+ length--;+ p->bom_strip_position++;+ } else {+ if (p->bom_strip_position == 0) {+ // no BOM in this document+ p->bom_strip_position = sizeof(UTF8_BOM);+ } else {+ // malformed BOM (prefix present, rest missing)+ p->bom_strip_position = 0xff;+ }+ }+ }+ p->curr_buf = buf;+ p->curr_buf_length = length;+ p->curr_buf_pos = 0;+ p->curr_buf_is_partial = is_partial;+}++static jv make_error(struct jv_parser*, const char *, ...) JV_PRINTF_LIKE(2, 3);++static jv make_error(struct jv_parser* p, const char *fmt, ...) {+ va_list ap;+ va_start(ap, fmt);+ jv e = jv_string_vfmt(fmt, ap);+ va_end(ap);+ if ((p->flags & JV_PARSE_STREAM_ERRORS))+ return JV_ARRAY(e, jv_copy(p->path));+ return jv_invalid_with_msg(e);+}++jv jv_parser_next(struct jv_parser* p) {+ if (p->eof)+ return jv_invalid();+ if (!p->curr_buf)+ return jv_invalid(); // Need a buffer+ if (p->bom_strip_position == 0xff) {+ if (!(p->flags & JV_PARSE_SEQ))+ return jv_invalid_with_msg(jv_string("Malformed BOM"));+ p->st =JV_PARSER_WAITING_FOR_RS;+ parser_reset(p);+ }+ jv value = jv_invalid();+ if ((p->flags & JV_PARSE_STREAMING) && stream_check_done(p, &value))+ return value;+ char ch;+ presult msg = 0;+ while (!msg && p->curr_buf_pos < p->curr_buf_length) {+ ch = p->curr_buf[p->curr_buf_pos++];+ if (p->st == JV_PARSER_WAITING_FOR_RS) {+ if (ch == '\n') {+ p->line++;+ p->column = 0;+ } else {+ p->column++;+ }+ if (ch == '\036')+ p->st = JV_PARSER_NORMAL;+ continue; // need to resync, wait for RS+ }+ msg = scan(p, ch, &value);+ }+ if (msg == OK) {+ return value;+ } else if (msg) {+ jv_free(value);+ if (ch != '\036' && (p->flags & JV_PARSE_SEQ)) {+ // Skip to the next RS+ p->st = JV_PARSER_WAITING_FOR_RS;+ value = make_error(p, "%s at line %d, column %d (need RS to resync)", msg, p->line, p->column);+ parser_reset(p);+ return value;+ }+ value = make_error(p, "%s at line %d, column %d", msg, p->line, p->column);+ parser_reset(p);+ if (!(p->flags & JV_PARSE_SEQ)) {+ // We're not parsing a JSON text sequence; throw this buffer away.+ // XXX We should fail permanently here.+ p->curr_buf = 0;+ p->curr_buf_pos = 0;+ } // Else ch must be RS; don't clear buf so we can start parsing again after this ch+ return value;+ } else if (p->curr_buf_is_partial) {+ assert(p->curr_buf_pos == p->curr_buf_length);+ // need another buffer+ return jv_invalid();+ } else {+ // at EOF+ p->eof = 1;+ assert(p->curr_buf_pos == p->curr_buf_length);+ jv_free(value);+ if (p->st == JV_PARSER_WAITING_FOR_RS)+ return make_error(p, "Unfinished abandoned text at EOF at line %d, column %d", p->line, p->column);+ if (p->st != JV_PARSER_NORMAL) {+ value = make_error(p, "Unfinished string at EOF at line %d, column %d", p->line, p->column);+ parser_reset(p);+ p->st = JV_PARSER_WAITING_FOR_RS;+ return value;+ }+ if ((msg = check_literal(p))) {+ value = make_error(p, "%s at EOF at line %d, column %d", msg, p->line, p->column);+ parser_reset(p);+ p->st = JV_PARSER_WAITING_FOR_RS;+ return value;+ }+ if (((p->flags & JV_PARSE_STREAMING) && p->stacklen != 0) ||+ (!(p->flags & JV_PARSE_STREAMING) && p->stackpos != 0)) {+ value = make_error(p, "Unfinished JSON term at EOF at line %d, column %d", p->line, p->column);+ parser_reset(p);+ p->st = JV_PARSER_WAITING_FOR_RS;+ return value;+ }+ // p->next is either invalid (nothing here, but no syntax error)+ // or valid (this is the value). either way it's the thing to return+ if ((p->flags & JV_PARSE_STREAMING) && jv_is_valid(p->next)) {+ value = JV_ARRAY(jv_copy(p->path), p->next); // except in streaming mode we've got to make it [path,value]+ } else {+ value = p->next;+ }+ p->next = jv_invalid();+ if ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && jv_get_kind(value) == JV_KIND_NUMBER) {+ jv_free(value);+ return make_error(p, "Potentially truncated top-level numeric value at EOF at line %d, column %d", p->line, p->column);+ }+ return value;+ }+}++jv jv_parse_sized(const char* string, int length) {+ struct jv_parser parser;+ parser_init(&parser, 0);+ 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_parse(const char* string) {+ return jv_parse_sized(string, strlen(string));+}
@@ -0,0 +1,348 @@+#include <assert.h>+#include <stdio.h>+#include <float.h>+#include <string.h>++#ifdef WIN32+#include <windows.h>+#include <io.h>+#include <fileapi.h>+#endif++#include "jv.h"+#include "jv_dtoa.h"+#include "jv_unicode.h"++#define ESC "\033"+#define COL(c) (ESC "[" c "m")+#define COLRESET (ESC "[0m")++// Color table. See https://en.wikipedia.org/wiki/ANSI_escape_code#Colors+// for how to choose these.+static const jv_kind color_kinds[] =+ {JV_KIND_NULL, JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER,+ JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT};+static const char* const colors[] =+ {COL("1;30"), COL("0;39"), COL("0;39"), COL("0;39"),+ COL("0;32"), COL("1;39"), COL("1;39")};+#define FIELD_COLOR COL("34;1")++static void put_buf(const char *s, int len, FILE *fout, jv *strout, int is_tty) {+ if (strout) {+ *strout = jv_string_append_buf(*strout, s, len);+ } else {+#ifdef WIN32+ /* See util.h */+ if (is_tty)+ WriteFile((HANDLE)_get_osfhandle(fileno(fout)), s, len, NULL, NULL);+ else+ fwrite(s, 1, len, fout);+#else+ fwrite(s, 1, len, fout);+#endif+ }+}++static void put_char(char c, FILE* fout, jv* strout, int T) {+ put_buf(&c, 1, fout, strout, T);+}++static void put_str(const char* s, FILE* fout, jv* strout, int T) {+ put_buf(s, strlen(s), fout, strout, T);+}++static void put_indent(int n, int flags, FILE* fout, jv* strout, int T) {+ if (flags & JV_PRINT_TAB) {+ while (n--)+ put_char('\t', fout, strout, T);+ } else {+ n *= ((flags & (JV_PRINT_SPACE0 | JV_PRINT_SPACE1 | JV_PRINT_SPACE2)) >> 8);+ while (n--)+ put_char(' ', fout, strout, T);+ }+}++static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S, int T) {+ assert(jv_get_kind(str) == JV_KIND_STRING);+ const char* i = jv_string_value(str);+ const char* end = i + jv_string_length_bytes(jv_copy(str));+ const char* cstart;+ int c = 0;+ char buf[32];+ put_char('"', F, S, T);+ while ((i = jvp_utf8_next((cstart = i), end, &c))) {+ assert(c != -1);+ int unicode_escape = 0;+ if (0x20 <= c && c <= 0x7E) {+ // printable ASCII+ if (c == '"' || c == '\\') {+ put_char('\\', F, S, T);+ }+ put_char(c, F, S, T);+ } else if (c < 0x20 || c == 0x7F) {+ // ASCII control character+ switch (c) {+ case '\b':+ put_char('\\', F, S, T);+ put_char('b', F, S, T);+ break;+ case '\t':+ put_char('\\', F, S, T);+ put_char('t', F, S, T);+ break;+ case '\r':+ put_char('\\', F, S, T);+ put_char('r', F, S, T);+ break;+ case '\n':+ put_char('\\', F, S, T);+ put_char('n', F, S, T);+ break;+ case '\f':+ put_char('\\', F, S, T);+ put_char('f', F, S, T);+ break;+ default:+ unicode_escape = 1;+ break;+ }+ } else {+ if (ascii_only) {+ unicode_escape = 1;+ } else {+ put_buf(cstart, i - cstart, F, S, T);+ }+ }+ if (unicode_escape) {+ if (c <= 0xffff) {+ snprintf(buf, sizeof(buf), "\\u%04x", c);+ } else {+ c -= 0x10000;+ snprintf(buf, sizeof(buf), "\\u%04x\\u%04x",+ 0xD800 | ((c & 0xffc00) >> 10),+ 0xDC00 | (c & 0x003ff));+ }+ put_str(buf, F, S, T);+ }+ }+ assert(c != -1);+ put_char('"', F, S, T);+}++static void put_refcnt(struct dtoa_context* C, int refcnt, FILE *F, jv* S, int T){+ char buf[JVP_DTOA_FMT_MAX_LEN];+ put_char(' ', F, S, T);+ put_char('(', F, S, T);+ put_str(jvp_dtoa_fmt(C, buf, refcnt), F, S, T);+ put_char(')', F, S, T);+}++static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {+ char buf[JVP_DTOA_FMT_MAX_LEN];+ const char* color = 0;+ double refcnt = (flags & JV_PRINT_REFCOUNT) ? jv_get_refcnt(x) - 1 : -1;+ if (flags & JV_PRINT_COLOR) {+ for (unsigned i=0; i<sizeof(color_kinds)/sizeof(color_kinds[0]); i++) {+ if (jv_get_kind(x) == color_kinds[i]) {+ color = colors[i];+ put_str(color, F, S, flags & JV_PRINT_ISATTY);+ break;+ }+ }+ }+ switch (jv_get_kind(x)) {+ default:+ case JV_KIND_INVALID:+ if (flags & JV_PRINT_INVALID) {+ jv msg = jv_invalid_get_msg(jv_copy(x));+ if (jv_get_kind(msg) == JV_KIND_STRING) {+ put_str("<invalid:", F, S, flags & JV_PRINT_ISATTY);+ jvp_dump_string(msg, flags | JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);+ put_str(">", F, S, flags & JV_PRINT_ISATTY);+ } else {+ put_str("<invalid>", F, S, flags & JV_PRINT_ISATTY);+ }+ } else {+ assert(0 && "Invalid value");+ }+ break;+ case JV_KIND_NULL:+ put_str("null", F, S, flags & JV_PRINT_ISATTY);+ break;+ case JV_KIND_FALSE:+ put_str("false", F, S, flags & JV_PRINT_ISATTY);+ break;+ case JV_KIND_TRUE:+ put_str("true", F, S, flags & JV_PRINT_ISATTY);+ break;+ case JV_KIND_NUMBER: {+ double d = jv_number_value(x);+ if (d != d) {+ // JSON doesn't have NaN, so we'll render it as "null"+ put_str("null", F, S, flags & JV_PRINT_ISATTY);+ } else {+ // Normalise infinities to something we can print in valid JSON+ if (d > DBL_MAX) d = DBL_MAX;+ if (d < -DBL_MAX) d = -DBL_MAX;+ put_str(jvp_dtoa_fmt(C, buf, d), F, S, flags & JV_PRINT_ISATTY);+ }+ break;+ }+ case JV_KIND_STRING:+ jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);+ if (flags & JV_PRINT_REFCOUNT)+ put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);+ break;+ case JV_KIND_ARRAY: {+ if (jv_array_length(jv_copy(x)) == 0) {+ put_str("[]", F, S, flags & JV_PRINT_ISATTY);+ break;+ }+ put_str("[", F, S, flags & JV_PRINT_ISATTY);+ if (flags & JV_PRINT_PRETTY) {+ put_char('\n', F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);+ }+ jv_array_foreach(x, i, elem) {+ if (i!=0) {+ if (flags & JV_PRINT_PRETTY) {+ put_str(",\n", F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);+ } else {+ put_str(",", F, S, flags & JV_PRINT_ISATTY);+ }+ }+ jv_dump_term(C, elem, flags, indent + 1, F, S);+ if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);+ }+ if (flags & JV_PRINT_PRETTY) {+ put_char('\n', F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);+ }+ if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);+ put_char(']', F, S, flags & JV_PRINT_ISATTY);+ if (flags & JV_PRINT_REFCOUNT)+ put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);+ break;+ }+ case JV_KIND_OBJECT: {+ if (jv_object_length(jv_copy(x)) == 0) {+ put_str("{}", F, S, flags & JV_PRINT_ISATTY);+ break;+ }+ put_char('{', F, S, flags & JV_PRINT_ISATTY);+ if (flags & JV_PRINT_PRETTY) {+ put_char('\n', F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);+ }+ int first = 1;+ int i = 0;+ jv keyset = jv_null();+ while (1) {+ jv key, value;+ if (flags & JV_PRINT_SORTED) {+ if (first) {+ keyset = jv_keys(jv_copy(x));+ i = 0;+ } else {+ i++;+ }+ if (i >= jv_array_length(jv_copy(keyset))) {+ jv_free(keyset);+ break;+ }+ key = jv_array_get(jv_copy(keyset), i);+ value = jv_object_get(jv_copy(x), jv_copy(key));+ } else {+ if (first) {+ i = jv_object_iter(x);+ } else {+ i = jv_object_iter_next(x, i);+ }+ if (!jv_object_iter_valid(x, i)) break;+ key = jv_object_iter_key(x, i);+ value = jv_object_iter_value(x, i);+ }++ if (!first) {+ if (flags & JV_PRINT_PRETTY){+ put_str(",\n", F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);+ } else {+ put_str(",", F, S, flags & JV_PRINT_ISATTY);+ }+ }+ if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);++ first = 0;+ if (color) put_str(FIELD_COLOR, F, S, flags & JV_PRINT_ISATTY);+ jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);+ jv_free(key);+ if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);++ if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);+ put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S, flags & JV_PRINT_ISATTY);+ if (color) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);++ jv_dump_term(C, value, flags, indent + 1, F, S);+ if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);+ }+ if (flags & JV_PRINT_PRETTY) {+ put_char('\n', F, S, flags & JV_PRINT_ISATTY);+ put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);+ }+ if (color) put_str(color, F, S, flags & JV_PRINT_ISATTY);+ put_char('}', F, S, flags & JV_PRINT_ISATTY);+ if (flags & JV_PRINT_REFCOUNT)+ put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);+ }+ }+ jv_free(x);+ if (color) {+ put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);+ }+}++void jv_dumpf(jv x, FILE *f, int flags) {+ struct dtoa_context C;+ jvp_dtoa_context_init(&C);+ jv_dump_term(&C, x, flags, 0, f, 0);+ jvp_dtoa_context_free(&C);+}++void jv_dump(jv x, int flags) {+ jv_dumpf(x, stdout, flags);+}++/* This one is nice for use in debuggers */+void jv_show(jv x, int flags) {+ if (flags == -1)+ flags = JV_PRINT_PRETTY | JV_PRINT_COLOR | JV_PRINT_INDENT_FLAGS(2);+ jv_dumpf(jv_copy(x), stderr, flags | JV_PRINT_INVALID);+ fflush(stderr);+}++jv jv_dump_string(jv x, int flags) {+ struct dtoa_context C;+ jvp_dtoa_context_init(&C);+ jv s = jv_string("");+ jv_dump_term(&C, x, flags, 0, 0, &s);+ jvp_dtoa_context_free(&C);+ return s;+}++char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) {+ x = jv_dump_string(x,0);+ const char* p = jv_string_value(x);+ const size_t len = strlen(p);+ strncpy(outbuf, p, bufsize);+ outbuf[bufsize - 1] = 0;+ if (len > bufsize - 1 && bufsize >= 4) {+ // Indicate truncation with '...'+ outbuf[bufsize - 2]='.';+ outbuf[bufsize - 3]='.';+ outbuf[bufsize - 4]='.';+ }+ jv_free(x);+ return outbuf;+}
@@ -0,0 +1,97 @@+#include <stdio.h>+#include <assert.h>+#include "jv_unicode.h"+#include "jv_utf8_tables.h"++const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {+ assert(in <= end);+ if (in == end) {+ return 0;+ }+ int codepoint = -1;+ unsigned char first = (unsigned char)in[0];+ int length = utf8_coding_length[first];+ if ((first & 0x80) == 0) {+ /* Fast-path for ASCII */+ codepoint = first;+ length = 1;+ } else if (length == 0 || length == UTF8_CONTINUATION_BYTE) {+ /* Bad single byte - either an invalid byte or an out-of-place continuation byte */+ length = 1;+ } else if (in + length > end) {+ /* String ends before UTF8 sequence ends */+ length = end - in;+ } else {+ codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];+ for (int i=1; i<length; i++) {+ unsigned ch = (unsigned char)in[i];+ if (utf8_coding_length[ch] != UTF8_CONTINUATION_BYTE){+ /* Invalid UTF8 sequence - not followed by the right number of continuation bytes */+ codepoint = -1;+ length = i;+ break;+ }+ codepoint = (codepoint << 6) | (ch & 0x3f);+ }+ if (codepoint < utf8_first_codepoint[length]) {+ /* Overlong UTF8 sequence */+ codepoint = -1;+ }+ if (0xD800 <= codepoint && codepoint <= 0xDFFF) {+ /* Surrogate codepoints can't be encoded in UTF8 */+ codepoint = -1;+ }+ if (codepoint > 0x10FFFF) {+ /* Outside Unicode range */+ codepoint = -1;+ }+ }+ assert(length > 0);+ *codepoint_ret = codepoint;+ return in + length;+}++int jvp_utf8_is_valid(const char* in, const char* end) {+ int codepoint;+ while ((in = jvp_utf8_next(in, end, &codepoint))) {+ if (codepoint == -1) return 0;+ }+ return 1;+}++/* Assumes startchar is the first byte of a valid character sequence */+int jvp_utf8_decode_length(char startchar) {+ if ((startchar & 0x80) == 0) return 1; // 0___ ____+ else if ((startchar & 0xE0) == 0xC0) return 2; // 110_ ____+ else if ((startchar & 0xF0) == 0xE0) return 3; // 1110 ____+ else return 4; // 1111 ____+}++int jvp_utf8_encode_length(int codepoint) {+ if (codepoint <= 0x7F) return 1;+ else if (codepoint <= 0x7FF) return 2;+ else if (codepoint <= 0xFFFF) return 3;+ else return 4;+}++int jvp_utf8_encode(int codepoint, char* out) {+ assert(codepoint >= 0 && codepoint <= 0x10FFFF);+ char* start = out;+ if (codepoint <= 0x7F) {+ *out++ = codepoint;+ } else if (codepoint <= 0x7FF) {+ *out++ = 0xC0 + ((codepoint & 0x7C0) >> 6);+ *out++ = 0x80 + ((codepoint & 0x03F));+ } else if(codepoint <= 0xFFFF) {+ *out++ = 0xE0 + ((codepoint & 0xF000) >> 12);+ *out++ = 0x80 + ((codepoint & 0x0FC0) >> 6);+ *out++ = 0x80 + ((codepoint & 0x003F));+ } else {+ *out++ = 0xF0 + ((codepoint & 0x1C0000) >> 18);+ *out++ = 0x80 + ((codepoint & 0x03F000) >> 12);+ *out++ = 0x80 + ((codepoint & 0x000FC0) >> 6);+ *out++ = 0x80 + ((codepoint & 0x00003F));+ }+ assert(out - start == jvp_utf8_encode_length(codepoint));+ return out - start;+}
@@ -0,0 +1,11 @@+#ifndef JV_UNICODE_H+#define JV_UNICODE_H++const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);+int jvp_utf8_is_valid(const char* in, const char* end);++int jvp_utf8_decode_length(char startchar);++int jvp_utf8_encode_length(int codepoint);+int jvp_utf8_encode(int codepoint, char* out);+#endif
@@ -0,0 +1,37 @@+#define UTF8_CONTINUATION_BYTE ((unsigned char)255)+static const unsigned char utf8_coding_length[] =+ {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,+ 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};+static const unsigned char utf8_coding_bits[] =+ {0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,+ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,+ 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};+static const int utf8_first_codepoint[] =+ {0x00, 0x00, 0x80, 0x800, 0x10000};
@@ -0,0 +1,2442 @@+#line 2 "lexer.c"++#line 4 "lexer.c"++#define YY_INT_ALIGNED short int++/* A lexical scanner generated by flex */++#define FLEX_SCANNER+#define YY_FLEX_MAJOR_VERSION 2+#define YY_FLEX_MINOR_VERSION 5+#define YY_FLEX_SUBMINOR_VERSION 35+#if YY_FLEX_SUBMINOR_VERSION > 0+#define FLEX_BETA+#endif++/* First, we deal with platform-specific or compiler-specific issues. */++/* begin standard C headers. */+#include <stdio.h>+#include <string.h>+#include <errno.h>+#include <stdlib.h>++/* end standard C headers. */++/* flex integer type definitions */++#ifndef FLEXINT_H+#define FLEXINT_H++/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */++#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L++/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,+ * if you want the limit (max/min) macros for int types. + */+#ifndef __STDC_LIMIT_MACROS+#define __STDC_LIMIT_MACROS 1+#endif++#include <inttypes.h>+typedef int8_t flex_int8_t;+typedef uint8_t flex_uint8_t;+typedef int16_t flex_int16_t;+typedef uint16_t flex_uint16_t;+typedef int32_t flex_int32_t;+typedef uint32_t flex_uint32_t;+#else+typedef signed char flex_int8_t;+typedef short int flex_int16_t;+typedef int flex_int32_t;+typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t;+typedef unsigned int flex_uint32_t;++/* Limits of integral types. */+#ifndef INT8_MIN+#define INT8_MIN (-128)+#endif+#ifndef INT16_MIN+#define INT16_MIN (-32767-1)+#endif+#ifndef INT32_MIN+#define INT32_MIN (-2147483647-1)+#endif+#ifndef INT8_MAX+#define INT8_MAX (127)+#endif+#ifndef INT16_MAX+#define INT16_MAX (32767)+#endif+#ifndef INT32_MAX+#define INT32_MAX (2147483647)+#endif+#ifndef UINT8_MAX+#define UINT8_MAX (255U)+#endif+#ifndef UINT16_MAX+#define UINT16_MAX (65535U)+#endif+#ifndef UINT32_MAX+#define UINT32_MAX (4294967295U)+#endif++#endif /* ! C99 */++#endif /* ! FLEXINT_H */++#ifdef __cplusplus++/* The "const" storage-class-modifier is valid. */+#define YY_USE_CONST++#else /* ! __cplusplus */++/* C99 requires __STDC__ to be defined as 1. */+#if defined (__STDC__)++#define YY_USE_CONST++#endif /* defined (__STDC__) */+#endif /* ! __cplusplus */++#ifdef YY_USE_CONST+#define yyconst const+#else+#define yyconst+#endif++/* Returned upon end-of-file. */+#define YY_NULL 0++/* Promotes a possibly negative, possibly signed char to an unsigned+ * integer for use as an array index. If the signed char is negative,+ * we want to instead treat it as an 8-bit unsigned char, hence the+ * double cast.+ */+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)++/* An opaque pointer. */+#ifndef YY_TYPEDEF_YY_SCANNER_T+#define YY_TYPEDEF_YY_SCANNER_T+typedef void* yyscan_t;+#endif++/* For convenience, these vars (plus the bison vars far below)+ are macros in the reentrant scanner. */+#define yyin yyg->yyin_r+#define yyout yyg->yyout_r+#define yyextra yyg->yyextra_r+#define yyleng yyg->yyleng_r+#define yytext yyg->yytext_r+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)+#define yy_flex_debug yyg->yy_flex_debug_r++/* Enter a start condition. This macro really ought to take a parameter,+ * but we do it the disgusting crufty way forced on us by the ()-less+ * definition of BEGIN.+ */+#define BEGIN yyg->yy_start = 1 + 2 *++/* Translate the current start state into a value that can be later handed+ * to BEGIN to return to the state. The YYSTATE alias is for lex+ * compatibility.+ */+#define YY_START ((yyg->yy_start - 1) / 2)+#define YYSTATE YY_START++/* Action number for EOF rule of a given start state. */+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)++/* Special action meaning "start processing a new file". */+#define YY_NEW_FILE jq_yyrestart(yyin ,yyscanner )++#define YY_END_OF_BUFFER_CHAR 0++/* Size of default input buffer. */+#ifndef YY_BUF_SIZE+#ifdef __ia64__+/* On IA-64, the buffer size is 16k, not 8k.+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.+ * Ditto for the __ia64__ case accordingly.+ */+#define YY_BUF_SIZE 32768+#else+#define YY_BUF_SIZE 16384+#endif /* __ia64__ */+#endif++/* The state buf must be large enough to hold one state per character in the main buffer.+ */+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))++#ifndef YY_TYPEDEF_YY_BUFFER_STATE+#define YY_TYPEDEF_YY_BUFFER_STATE+typedef struct yy_buffer_state *YY_BUFFER_STATE;+#endif++#define EOB_ACT_CONTINUE_SCAN 0+#define EOB_ACT_END_OF_FILE 1+#define EOB_ACT_LAST_MATCH 2++ #define YY_LESS_LINENO(n)+ +/* Return all but the first "n" matched characters back to the input stream. */+#define yyless(n) \+ do \+ { \+ /* Undo effects of setting up yytext. */ \+ int yyless_macro_arg = (n); \+ YY_LESS_LINENO(yyless_macro_arg);\+ *yy_cp = yyg->yy_hold_char; \+ YY_RESTORE_YY_MORE_OFFSET \+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \+ } \+ while ( 0 )++#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )++#ifndef YY_TYPEDEF_YY_SIZE_T+#define YY_TYPEDEF_YY_SIZE_T+typedef size_t yy_size_t;+#endif++#ifndef YY_STRUCT_YY_BUFFER_STATE
The diff stops at 1024 KiB. git show 0c93eb3 shows the rest.